Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Show data from DB #28

Closed
wants to merge 57 commits into from
Closed
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
09b3ed5
Add postgres yml file
fkrause98 Jul 4, 2023
2e2cc32
Add draft GenServer and block migration
fkrause98 Jul 4, 2023
9765ea0
Add tx migration, schema and block schema
fkrause98 Jul 5, 2023
167f1ce
Working commit
fkrause98 Jul 5, 2023
61acd85
Update blocks and transactions schemas
fkrause98 Jul 5, 2023
ea80955
Use BlockFetcher only in Prod
fkrause98 Jul 5, 2023
3d5313a
Move block schema, update some fetcher code, mix format
fkrause98 Jul 5, 2023
6590ea9
Update tx fields
fkrause98 Jul 6, 2023
0637600
Lower fetch interval
fkrause98 Jul 6, 2023
42eef27
Mix format + update tx fields
fkrause98 Jul 6, 2023
87eb1d2
Remove unnecesary comment
fkrause98 Jul 6, 2023
f05f03f
Add block fetcher tests
fkrause98 Jul 6, 2023
f509beb
Add postgres to CI job
fkrause98 Jul 6, 2023
acc219a
Format
fkrause98 Jul 6, 2023
a7be459
Add infura api key secret read
fkrause98 Jul 6, 2023
9fdc79e
Undo wrong template change
fkrause98 Jul 6, 2023
05d056a
Format
fkrause98 Jul 6, 2023
39eaa51
Update makefile and readme
fkrause98 Jul 6, 2023
5568206
Fix CI
fkrause98 Jul 6, 2023
a4ec4a9
Add seed
fkrause98 Jul 6, 2023
b4b8593
Update block index to show DB data
fkrause98 Jul 6, 2023
5f2d956
Make block fetching take transaction receipts
fkrause98 Jul 10, 2023
cd52b81
Update function comment
fkrause98 Jul 10, 2023
0426e62
Merge branch 'main' into show-db-data
fkrause98 Jul 10, 2023
cc28961
Add create-seed make target
fkrause98 Jul 10, 2023
a26d3c7
Delete seed.sql
fkrause98 Jul 10, 2023
0916fbe
Make block index show DB data
fkrause98 Jul 10, 2023
f3c8cca
Show block detail from DB
fkrause98 Jul 10, 2023
6295772
Format
fkrause98 Jul 10, 2023
44d578c
Show tx from block detail
fkrause98 Jul 10, 2023
93de516
Use DB data on tx index
fkrause98 Jul 10, 2023
850d8cd
Use DB data on TX detail
fkrause98 Jul 10, 2023
e36ff4c
Add seeds and db data use to index
fkrause98 Jul 10, 2023
a101e98
Delete seed.sql
fkrause98 Jul 10, 2023
ac9f811
Add sql dump for seeds
fkrause98 Jul 10, 2023
11d48f2
Add make seed comment
fkrause98 Jul 11, 2023
b596e94
Fix block and home pages
fkrause98 Jul 11, 2023
d77b57f
Update transaction and receipts validations
fkrause98 Jul 11, 2023
6a08eae
Remove unused key
fkrause98 Jul 11, 2023
7aa0315
Update block fetcher tests
fkrause98 Jul 11, 2023
c41cff1
Update seeds
fkrause98 Jul 11, 2023
05d028d
Merge branch 'store-tx-receipts' into show-db-data
fkrause98 Jul 11, 2023
9fda339
Fix block access error
fkrause98 Jul 11, 2023
83dfa0c
Modify some receipt fields
fkrause98 Jul 11, 2023
4f33e54
Fix field error
fkrause98 Jul 11, 2023
6817e66
Merge branch 'store-tx-receipts' into show-db-data
fkrause98 Jul 11, 2023
089f515
Use tx receipt data from db
fkrause98 Jul 11, 2023
6689994
Remove inspect
fkrause98 Jul 11, 2023
f2dd62e
Mix format
fkrause98 Jul 11, 2023
cb4cb55
Merge branch 'main' into show-db-data
fkrause98 Jul 12, 2023
c3dadd9
Fix txs index
fkrause98 Jul 12, 2023
e1059ca
Fix tx detail
fkrause98 Jul 12, 2023
f86a949
Format
fkrause98 Jul 12, 2023
473a069
Fix block detail
fkrause98 Jul 12, 2023
3eb3c2d
More fixes
fkrause98 Jul 12, 2023
f1c6fd5
Uncomment fetcher for prod
fkrause98 Jul 24, 2023
f91f9f8
Save original_json for tx receipt
fkrause98 Jul 24, 2023
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
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ stop-db:

deps-get:
mix deps.get

db_container := $(shell docker ps -aqf name=starknet_explorer_dev_db)
seed: db
cat ./priv/repo/seed.sql | docker exec -i $(db_container) psql -U postgres -d starknet_explorer_dev
create-seed: db
docker exec -i $(db_container) pg_dump --column-inserts --data-only -d starknet_explorer_dev -U postgres > ./priv/repo/seed.sql
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ make setup run
```

This will start postgres, create the database and start a web app in `localhost:4000`.
For local development, you can seed the db with

```
make seed
```

From now on, if you want to restart the app, you can just do:
```
Expand Down
84 changes: 68 additions & 16 deletions lib/starknet_explorer/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ defmodule StarknetExplorer.Block do
use Ecto.Schema
import Ecto.Query
import Ecto.Changeset
alias StarknetExplorer.{Repo, Transaction}
alias StarknetExplorer.{Repo, Transaction, Block}
alias StarknetExplorer.TransactionReceipt, as: Receipt
require Logger
@primary_key {:number, :integer, []}
schema "blocks" do
Expand All @@ -12,7 +13,7 @@ defmodule StarknetExplorer.Block do
field :new_root, :string
field :timestamp, :integer
field :sequencer_address, :string
field :original_json, :binary
field :original_json, :binary, load_in_query: false
has_many :transactions, StarknetExplorer.Transaction, foreign_key: :block_number
timestamps()
end
Expand Down Expand Up @@ -45,9 +46,10 @@ defmodule StarknetExplorer.Block do
end

@doc """
Given a block from the RPC response, insert it into the database.
Given a block from the RPC response, and transactions receipts
insert them into the DB.
"""
def insert_from_rpc_response(block = %{"transactions" => txs}) when is_map(block) do
def insert_from_rpc_response(block = %{"transactions" => txs}, receipts) when is_map(block) do
# Store the original response, in case we need it
# in the future, as a binary blob.
original_json = :erlang.term_to_binary(block)
Expand All @@ -65,23 +67,31 @@ defmodule StarknetExplorer.Block do
end)
|> Map.put("original_json", original_json)

# Validate each transaction before inserting
# the block into postgres. I think
# this can be done with cast_assoc.
transactions =
txs
|> Enum.map(fn tx ->
Transaction.changeset(%Transaction{}, Map.put(tx, "block_number", block["number"]))
end)
transaction_result =
StarknetExplorer.Repo.transaction(fn ->
block_changeset = Block.changeset(%Block{original_json: original_json}, block)

{:ok, block} = Repo.insert(block_changeset)

changeset = changeset(%StarknetExplorer.Block{transactions: transactions}, block)
_txs_changeset =
Enum.map(txs, fn tx ->
tx =
Ecto.build_assoc(block, :transactions, tx)
|> Transaction.changeset(tx)
|> Repo.insert!()

Ecto.build_assoc(tx, :receipt, receipts[tx.hash])
|> Receipt.changeset(receipts[tx.hash])
|> Repo.insert!()
end)
end)

case Repo.insert(changeset) do
case transaction_result do
{:ok, _} ->
:ok

{:error, error} ->
Logger.error("Failed to insert block: #{inspect(error)}")
{:error, err} ->
Logger.error("Error inserting block: #{inspect(err)}")
:error
end
end
Expand All @@ -101,4 +111,46 @@ defmodule StarknetExplorer.Block do
[%{number: number}] -> number
end
end

@doc """
Returns the n latests blocks
"""
def latest_n_blocks(n \\ 20) do
query =
from b in Block,
order_by: [desc: b.number],
limit: ^n

Repo.all(query)
end

@doc """
Returns the n latests blocks
"""
def latest_n_blocks_with_txs(n \\ 20) do
query =
from b in Block,
order_by: [desc: b.number],
limit: ^n

Repo.all(query) |> Repo.preload(:transactions)
end

def get_by_hash_with_txs(hash) do
query =
from b in Block,
where: b.hash == ^hash,
preload: :transactions

Repo.one(query)
end

def get_by_num_with_txs(number) do
query =
from b in Block,
where: b.number == ^number,
preload: :transactions

Repo.one(query)
end
end
11 changes: 9 additions & 2 deletions lib/starknet_explorer/block_fetcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,15 @@ defmodule StarknetExplorer.BlockFetcher do
# fetch a new block, else do nothing.
if curr_height + 10 >= state.latest_block_fetched do
case fetch_block(state.latest_block_fetched + 1) do
{:ok, block = %{"block_number" => new_block_number}} ->
:ok = Block.insert_from_rpc_response(block)
{:ok, block = %{"block_number" => new_block_number, "transactions" => transactions}} ->
receipts =
transactions
|> Map.new(fn %{"transaction_hash" => tx_hash} ->
{:ok, receipt} = Rpc.get_transaction_receipt(tx_hash)
{tx_hash, receipt}
end)

:ok = Block.insert_from_rpc_response(block, receipts)
Logger.info("Inserted new block: #{new_block_number}")
Process.send_after(self(), :fetch_and_store, @fetch_interval)
{:noreply, %{state | block_height: curr_height, latest_block_fetched: new_block_number}}
Expand Down
32 changes: 24 additions & 8 deletions lib/starknet_explorer/transaction.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule StarknetExplorer.Transaction do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias StarknetExplorer.{Transaction, Repo, TransactionReceipt}

@l1_handler_tx_fields [
:hash,
Expand All @@ -27,7 +29,6 @@ defmodule StarknetExplorer.Transaction do
@invoke_v1_tx_fields [
:calldata,
:sender_address,
:entry_point_selector,
:hash,
:max_fee,
:nonce,
Expand Down Expand Up @@ -65,16 +66,15 @@ defmodule StarknetExplorer.Transaction do
:nonce,
:signature,
:type,
:type,
:version
]

@fields @l1_handler_tx_fields ++
@invoke_tx_fields ++
@declare_tx_fields ++ @deploy_contract_tx_fields ++ @deploy_account_tx_fields

@primary_key {:hash, :string, []}
schema "transactions" do
field :hash, :string
field :constructor_calldata, {:array, :string}
field :class_hash, :string
field :type, :string
Expand All @@ -91,23 +91,34 @@ defmodule StarknetExplorer.Transaction do
field :sender_address, :string
field :calldata, {:array, :string}
belongs_to :block, StarknetExplorer.Block, foreign_key: :block_number
has_one :receipt, TransactionReceipt
timestamps()
end

def changeset(tx, attrs) do
def changeset(tx, attrs = %{"transaction_hash" => hash}) do
attrs = rename_rpc_fields(attrs)

tx
|> cast(
attrs,
@fields
)
|> Ecto.Changeset.change(%{hash: hash})
|> unique_constraint([:hash])
|> validate_according_to_tx_type(attrs)
|> unique_constraint(:hash)
end

def get_by_hash_with_receipt(hash) do
query =
from tx in Transaction,
where: tx.hash == ^hash,
preload: [:receipt]

Repo.one(query)
end

defp rename_rpc_fields(rpc_tx = %{"transaction_hash" => th}) do
rpc_tx |> Map.put("hash", th)
rpc_tx |> Map.delete("transaction_hash") |> Map.put("hash", th)
end

defp validate_according_to_tx_type(changeset, _tx = %{"type" => "INVOKE", "version" => "0x1"}) do
Expand All @@ -120,12 +131,17 @@ defmodule StarknetExplorer.Transaction do
|> validate_required(@invoke_tx_fields)
end

defp validate_according_to_tx_type(changeset, tx = %{"type" => "DEPLOY", "max_fee" => _}) do
defp validate_according_to_tx_type(changeset, _tx = %{"type" => "DEPLOY", "max_fee" => _}) do
changeset
|> validate_required(@deploy_account_tx_fields)
end

defp validate_according_to_tx_type(changeset, _tx = %{"type" => "DEPLOY_ACCOUNT"}) do
changeset
|> validate_required(@deploy_account_tx_fields)
end

defp validate_according_to_tx_type(changeset, tx = %{"type" => "DEPLOY"}) do
defp validate_according_to_tx_type(changeset, _tx = %{"type" => "DEPLOY"}) do
changeset
|> validate_required(@deploy_contract_tx_fields)
end
Expand Down
Loading