Skip to content

Commit e01cf5a

Browse files
MegaRedHandArkenan
andauthored
feat: persist deposits snapshot in DB (#1019)
Co-authored-by: Tomás Arjovsky <[email protected]>
1 parent c70a75c commit e01cf5a

File tree

6 files changed

+75
-13
lines changed

6 files changed

+75
-13
lines changed

lib/lambda_ethereum_consensus/beacon/store_setup.ex

+7-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,13 @@ defmodule LambdaEthereumConsensus.Beacon.StoreSetup do
8888

8989
@spec get_deposit_snapshot!(store_setup_strategy()) :: DepositTreeSnapshot.t() | nil
9090
def get_deposit_snapshot!({:checkpoint_sync_url, url}), do: fetch_deposit_snapshot(url)
91-
def get_deposit_snapshot!(:db), do: nil
91+
92+
def get_deposit_snapshot!(:db) do
93+
case StoreDb.fetch_deposits_snapshot() do
94+
{:ok, snapshot} -> snapshot
95+
_ -> nil
96+
end
97+
end
9298

9399
def get_deposit_snapshot!({:file, %{eth1_data: %Eth1Data{} = eth1_data}}) do
94100
if eth1_data.deposit_count == 0 do

lib/lambda_ethereum_consensus/execution/execution_chain.ex

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule LambdaEthereumConsensus.Execution.ExecutionChain do
77
use GenServer
88

99
alias LambdaEthereumConsensus.Execution.ExecutionClient
10+
alias LambdaEthereumConsensus.Store.StoreDb
1011
alias Types.Deposit
1112
alias Types.DepositTree
1213
alias Types.DepositTreeSnapshot
@@ -23,6 +24,9 @@ defmodule LambdaEthereumConsensus.Execution.ExecutionChain do
2324
GenServer.call(__MODULE__, {:get_eth1_vote, slot})
2425
end
2526

27+
@spec get_eth1_vote(Types.slot()) :: DepositTreeSnapshot.t()
28+
def get_deposit_snapshot(), do: GenServer.call(__MODULE__, :get_deposit_snapshot)
29+
2630
@spec get_deposits(Eth1Data.t(), Eth1Data.t(), Range.t()) ::
2731
{:ok, [Deposit.t()] | nil} | {:error, any}
2832
def get_deposits(current_eth1_data, eth1_vote, deposit_range) do
@@ -53,6 +57,8 @@ defmodule LambdaEthereumConsensus.Execution.ExecutionChain do
5357

5458
updated_state = Enum.reduce(eth1_votes, state, &update_state_with_vote(&2, &1))
5559

60+
StoreDb.persist_deposits_snapshot(snapshot)
61+
5662
{:ok, updated_state}
5763
end
5864

@@ -61,6 +67,11 @@ defmodule LambdaEthereumConsensus.Execution.ExecutionChain do
6167
{:reply, compute_eth1_vote(state, slot), state}
6268
end
6369

70+
@impl true
71+
def handle_call(:get_deposit_snapshot, _from, state) do
72+
{:reply, DepositTree.get_snapshot(state.deposit_tree), state}
73+
end
74+
6475
def handle_call({:get_deposits, current_eth1_data, eth1_vote, deposit_range}, _from, state) do
6576
votes = state.eth1_data_votes
6677

@@ -142,6 +153,8 @@ defmodule LambdaEthereumConsensus.Execution.ExecutionChain do
142153
{:ok, deposits} <- ExecutionClient.get_deposit_logs(start_block..end_block) do
143154
# TODO: check if the result should be sorted by index
144155
deposit_tree = DepositTree.finalize(state.deposit_tree, old_eth1_data, start_block)
156+
# TODO: delay persisting until it's finalized
157+
deposit_tree |> DepositTree.get_snapshot() |> StoreDb.persist_deposits_snapshot()
145158
{:ok, update_tree_with_deposits(deposit_tree, deposits)}
146159
end
147160
end

lib/lambda_ethereum_consensus/store/store_db.ex

+22-7
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,32 @@ defmodule LambdaEthereumConsensus.Store.StoreDb do
55
alias LambdaEthereumConsensus.Store.Db
66

77
@store_prefix "store"
8+
@snapshot_prefix "snapshot"
89

910
@spec fetch_store() :: {:ok, Types.Store.t()} | :not_found
10-
def fetch_store() do
11-
with {:ok, encoded_store} <- Db.get(@store_prefix) do
12-
{:ok, :erlang.binary_to_term(encoded_store)}
13-
end
14-
end
11+
def fetch_store(), do: get(@store_prefix)
1512

1613
@spec persist_store(Types.Store.t()) :: :ok
1714
def persist_store(%Types.Store{} = store) do
18-
# Compress the store before storing it. This doubles the time it takes to dump, but reduces size by 5 times.
19-
Db.put(@store_prefix, :erlang.term_to_binary(store, [{:compressed, 1}]))
15+
put(@store_prefix, store)
16+
end
17+
18+
@spec fetch_deposits_snapshot() :: {:ok, Types.DepositTreeSnapshot.t()} | :not_found
19+
def fetch_deposits_snapshot(), do: get(@snapshot_prefix)
20+
21+
@spec persist_deposits_snapshot(Types.DepositTreeSnapshot.t()) :: :ok
22+
def persist_deposits_snapshot(%Types.DepositTreeSnapshot{} = snapshot) do
23+
put(@snapshot_prefix, snapshot)
24+
end
25+
26+
defp get(key) do
27+
with {:ok, value} <- Db.get(key) do
28+
{:ok, :erlang.binary_to_term(value)}
29+
end
30+
end
31+
32+
defp put(key, value) do
33+
# Compress before storing. This doubles the time it takes to dump, but reduces size by 5 times.
34+
Db.put(key, :erlang.term_to_binary(value, [{:compressed, 1}]))
2035
end
2136
end

lib/types/deposit_tree.ex

+25-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ defmodule Types.DepositTree do
4545
}
4646
end
4747

48+
@spec get_snapshot(t()) :: DepositTreeSnapshot.t()
49+
def get_snapshot(%__MODULE__{} = tree) do
50+
finalized = get_finalized(tree.inner)
51+
deposit_root = get_root(tree)
52+
{el_hash, el_height} = tree.finalized_execution_block
53+
54+
%DepositTreeSnapshot{
55+
finalized: finalized,
56+
deposit_root: deposit_root,
57+
deposit_count: tree.deposit_count,
58+
execution_block_hash: el_hash,
59+
execution_block_height: el_height
60+
}
61+
end
62+
4863
@spec finalize(t(), Eth1Data.t(), non_neg_integer()) :: t()
4964
def finalize(%__MODULE__{} = tree, %Eth1Data{} = eth1_data, execution_block_height) do
5065
finalized_block = {eth1_data.block_hash, execution_block_height}
@@ -55,7 +70,7 @@ defmodule Types.DepositTree do
5570
@spec get_deposit(t(), non_neg_integer()) :: {:ok, Deposit.t()} | {:error, String.t()}
5671
def get_deposit(%__MODULE__{} = tree, index) do
5772
cond do
58-
index < get_finalized(tree.inner) ->
73+
index < count_finalized(tree.inner) ->
5974
{:error, "deposit already finalized"}
6075

6176
index >= tree.deposit_count ->
@@ -171,10 +186,15 @@ defmodule Types.DepositTree do
171186
defp full?({:zero, _}), do: false
172187
defp full?(_), do: true
173188

174-
defp get_finalized({:finalized, {_, count}}), do: count
175-
defp get_finalized({:node, {left, right}}), do: get_finalized(left) + get_finalized(right)
176-
defp get_finalized({:leaf, _}), do: 0
177-
defp get_finalized({:zero, _}), do: 0
189+
defp count_finalized({:finalized, {_, count}}), do: count
190+
defp count_finalized({:node, {left, right}}), do: count_finalized(left) + count_finalized(right)
191+
defp count_finalized({:leaf, _}), do: 0
192+
defp count_finalized({:zero, _}), do: 0
193+
194+
defp get_finalized({:finalized, {hash, _}}), do: [hash]
195+
defp get_finalized({:node, {left, right}}), do: get_finalized(right) ++ get_finalized(left)
196+
defp get_finalized({:leaf, _}), do: []
197+
defp get_finalized({:zero, _}), do: []
178198

179199
defp mix_in_length(%__MODULE__{deposit_count: count}),
180200
do: SszEx.hash_tree_root!(count, TypeAliases.uint64())

test/unit/deposit_tree_test.exs

+2
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,7 @@ defmodule Unit.DepositTreeTest do
105105
|> DepositTree.finalize(eth1_data, @snapshot_2.execution_block_height)
106106

107107
assert tree == DepositTree.from_snapshot(@snapshot_2)
108+
109+
assert DepositTree.get_snapshot(tree) == @snapshot_2
108110
end
109111
end

test/unit/ssz_ex_test.exs

+6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ defmodule Unit.SSZExTest do
1313

1414
use ExUnit.Case
1515

16+
setup_all do
17+
Application.fetch_env!(:lambda_ethereum_consensus, ChainSpec)
18+
|> Keyword.put(:config, MainnetConfig)
19+
|> then(&Application.put_env(:lambda_ethereum_consensus, ChainSpec, &1))
20+
end
21+
1622
def assert_roundtrip(serialized, deserialized, schema) do
1723
assert {:ok, ^serialized} = SszEx.encode(deserialized, schema)
1824
assert {:ok, deserialized} === SszEx.decode(serialized, schema)

0 commit comments

Comments
 (0)