From 3c2fba60efcb46842613c21fce956e633e16f9d9 Mon Sep 17 00:00:00 2001 From: Pawel Gebal Date: Tue, 25 Aug 2020 16:46:03 +0200 Subject: [PATCH] test: test ife deletions --- .../lib/omg_watcher/exit_processor.ex | 6 +- .../lib/omg_watcher/exit_processor/core.ex | 21 ++++--- .../exit_processor/in_flight_exit_info.ex | 8 ++- .../core/state_interaction_test.exs | 17 +++++ .../omg_watcher/exit_processor/core_test.exs | 38 +++++++++++ .../in_flight_exit_info_test.exs | 63 +++++++++++++++++++ priv/cabbage | 2 +- 7 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 apps/omg_watcher/test/omg_watcher/exit_processor/in_flight_exit_info_test.exs diff --git a/apps/omg_watcher/lib/omg_watcher/exit_processor.ex b/apps/omg_watcher/lib/omg_watcher/exit_processor.ex index bdacacce6f..1593433412 100644 --- a/apps/omg_watcher/lib/omg_watcher/exit_processor.ex +++ b/apps/omg_watcher/lib/omg_watcher/exit_processor.ex @@ -38,7 +38,6 @@ defmodule OMG.Watcher.ExitProcessor do alias OMG.Watcher.ExitProcessor alias OMG.Watcher.ExitProcessor.Core alias OMG.Watcher.ExitProcessor.ExitInfo - alias OMG.Watcher.ExitProcessor.InFlightExitInfo alias OMG.Watcher.ExitProcessor.StandardExit alias OMG.Watcher.ExitProcessor.Tools @@ -382,9 +381,8 @@ defmodule OMG.Watcher.ExitProcessor do if not Enum.empty?(deletions), do: Logger.info("Recognized #{Enum.count(deletions)} deletions: #{inspect(deletions)}") - {new_state, deleted_exits, db_updates} = Core.delete_in_flight_exits(state, deletions) - input_utxos = InFlightExitInfo.get_inputs(deleted_exits) - {:ok, db_updates_from_state, _validities} = State.exit_utxos(input_utxos) + {new_state, deleted_utxos, db_updates} = Core.delete_in_flight_exits(state, deletions) + {:ok, db_updates_from_state, _validities} = State.exit_utxos(deleted_utxos) {:reply, {:ok, db_updates ++ db_updates_from_state}, new_state} end diff --git a/apps/omg_watcher/lib/omg_watcher/exit_processor/core.ex b/apps/omg_watcher/lib/omg_watcher/exit_processor/core.ex index 65dfc5ac86..ac6e8803dc 100644 --- a/apps/omg_watcher/lib/omg_watcher/exit_processor/core.ex +++ b/apps/omg_watcher/lib/omg_watcher/exit_processor/core.ex @@ -649,17 +649,24 @@ defmodule OMG.Watcher.ExitProcessor.Core do def delete_in_flight_exits(%__MODULE__{in_flight_exits: ifes} = state, deletions) do exit_ids = deletions - |> Enum.map(& &1.exit_id) + |> Enum.map(fn %{exit_id: exit_id} -> InFlightExitInfo.to_contract_id(exit_id) end) |> MapSet.new() - deleted_ifes_by_key = Enum.filter(ifes, fn {_, ife} -> MapSet.member?(exit_ids, ife.contract_id) end) + deleted_ifes_by_key = + ifes + |> Enum.filter(fn {_, ife} -> MapSet.member?(exit_ids, ife.contract_id) end) + |> Map.new() - keys_to_delete = Map.keys(deleted_ifes_by_key) - updated_ifes = Map.drop(ifes, keys_to_delete) + deleted_keys = Map.keys(deleted_ifes_by_key) + updated_ifes = Map.drop(ifes, deleted_keys) + + deleted_utxos = + deleted_ifes_by_key + |> Map.values() + |> InFlightExitInfo.get_inputs() - deleted_ifes = Map.values(deleted_ifes_by_key) - db_updates = Enum.map(keys_to_delete, fn key -> {:delete, :in_flight_exit_info, key} end) + db_updates = Enum.map(deleted_keys, fn key -> {:delete, :in_flight_exit_info, key} end) - {%{state | ifes: updated_ifes}, deleted_ifes, db_updates} + {%{state | in_flight_exits: updated_ifes}, deleted_utxos, db_updates} end end diff --git a/apps/omg_watcher/lib/omg_watcher/exit_processor/in_flight_exit_info.ex b/apps/omg_watcher/lib/omg_watcher/exit_processor/in_flight_exit_info.ex index 2b782cbfc1..41198ca47b 100644 --- a/apps/omg_watcher/lib/omg_watcher/exit_processor/in_flight_exit_info.ex +++ b/apps/omg_watcher/lib/omg_watcher/exit_processor/in_flight_exit_info.ex @@ -272,7 +272,7 @@ defmodule OMG.Watcher.ExitProcessor.InFlightExitInfo do @spec get_inputs(list(t())) :: list(Utxo.Position.t()) def get_inputs(ifes) do ifes - |> Enum.map(fn %{tx: tx} -> Transaction.get_inputs(tx) end) + |> Enum.map(fn %{input_utxos_pos: pos} -> pos end) |> List.flatten() end @@ -434,6 +434,12 @@ defmodule OMG.Watcher.ExitProcessor.InFlightExitInfo do }), do: is_older?(seen_in_pos, oldest_competitor_pos) + @doc """ + Converts integer to in-flight exit contract id + """ + @spec to_contract_id(non_neg_integer) :: <<_::192>> + def to_contract_id(id), do: <> + @doc """ Checks if the competitor being seen at `competitor_pos` (`nil` if unseen) is viable to challenge with, considering the current state of the IFE - that is, only if it is older than IFE tx's inclusion and other competitors diff --git a/apps/omg_watcher/test/omg_watcher/exit_processor/core/state_interaction_test.exs b/apps/omg_watcher/test/omg_watcher/exit_processor/core/state_interaction_test.exs index 2eb35b4dec..aaafd75688 100644 --- a/apps/omg_watcher/test/omg_watcher/exit_processor/core/state_interaction_test.exs +++ b/apps/omg_watcher/test/omg_watcher/exit_processor/core/state_interaction_test.exs @@ -289,6 +289,23 @@ defmodule OMG.Watcher.ExitProcessor.Core.StateInteractionTest do assert [_] = Core.get_active_in_flight_exits(processor) end + test "deleting in-flight exits works with State", + %{processor_empty: processor, state_empty: state, alice: alice} do + ife_exit_tx = TestHelper.create_recovered([{1, 0, 0, alice}], @eth, [{alice, 9}]) + ife_id = 1 + + state = TestHelper.do_deposit(state, alice, %{amount: 10, currency: @eth, blknum: 1}) + {:ok, _, state} = State.Core.form_block(state) + + {_processor, deleted_utxos, _db_updates} = + processor + |> start_ife_from(ife_exit_tx, exit_id: ife_id) + |> Core.delete_in_flight_exits([%{exit_id: ife_id}]) + + assert {:ok, {[{:delete, :utxo, _}], {[{:utxo_position, 1, 0, 0}], []}}, _} = + State.Core.exit_utxos(deleted_utxos, state) + end + defp mock_utxo_exists(%ExitProcessor.Request{utxos_to_check: positions} = request, state) do %{request | utxo_exists_result: positions |> Enum.map(&State.Core.utxo_exists?(&1, state))} end diff --git a/apps/omg_watcher/test/omg_watcher/exit_processor/core_test.exs b/apps/omg_watcher/test/omg_watcher/exit_processor/core_test.exs index d9451bbf8c..2fd9fda0d5 100644 --- a/apps/omg_watcher/test/omg_watcher/exit_processor/core_test.exs +++ b/apps/omg_watcher/test/omg_watcher/exit_processor/core_test.exs @@ -335,4 +335,42 @@ defmodule OMG.Watcher.ExitProcessor.CoreTest do db_value_map end end + + describe "delete_in_flight_exits/2" do + test "returns deleted utxos and database updates", %{processor_empty: processor, alice: alice} do + ife_exit_tx1 = TestHelper.create_recovered([{1, 0, 0, alice}], @eth, [{alice, 9}]) + ife_id1 = 1 + tx_hash1 = Transaction.raw_txhash(ife_exit_tx1) + ife_exit_tx2 = TestHelper.create_recovered([{2, 0, 1, alice}, {2, 0, 2, alice}], @eth, [{alice, 9}]) + ife_id2 = 2 + ife_exit_tx3 = TestHelper.create_recovered([{3, 0, 1, alice}, {3, 0, 2, alice}], @eth, [{alice, 9}]) + ife_id3 = 3 + tx_hash3 = Transaction.raw_txhash(ife_exit_tx3) + + {_processor, deleted_utxos, db_updates} = + processor + |> start_ife_from(ife_exit_tx1, exit_id: ife_id1) + |> start_ife_from(ife_exit_tx2, exit_id: ife_id2) + |> start_ife_from(ife_exit_tx3, exit_id: ife_id3) + |> Core.delete_in_flight_exits([%{exit_id: ife_id1}, %{exit_id: ife_id3}]) + + assert Enum.sort(deleted_utxos) == + Enum.sort([{:utxo_position, 3, 0, 1}, {:utxo_position, 3, 0, 2}, {:utxo_position, 1, 0, 0}]) + + assert Enum.sort(db_updates) == + Enum.sort([{:delete, :in_flight_exit_info, tx_hash1}, {:delete, :in_flight_exit_info, tx_hash3}]) + end + + test "deletes in-flight exits from processor", %{processor_empty: processor, alice: alice} do + ife_exit_tx = TestHelper.create_recovered([{1, 0, 0, alice}], @eth, [{alice, 9}]) + ife_id = 1 + + {processor, _deleted_utxos, _db_updates} = + processor + |> start_ife_from(ife_exit_tx, exit_id: ife_id) + |> Core.delete_in_flight_exits([%{exit_id: ife_id}]) + + assert Enum.empty?(processor.in_flight_exits) + end + end end diff --git a/apps/omg_watcher/test/omg_watcher/exit_processor/in_flight_exit_info_test.exs b/apps/omg_watcher/test/omg_watcher/exit_processor/in_flight_exit_info_test.exs new file mode 100644 index 0000000000..2f95acf32e --- /dev/null +++ b/apps/omg_watcher/test/omg_watcher/exit_processor/in_flight_exit_info_test.exs @@ -0,0 +1,63 @@ +# Copyright 2019-2020 OmiseGO Pte Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +defmodule OMG.Watcher.ExitProcessor.InFlightExitInfoTest do + @moduledoc false + + use OMG.Watcher.ExitProcessor.Case, async: true + + alias OMG.State.Transaction + alias OMG.Utxo.Position + alias OMG.Watcher.ExitProcessor.InFlightExitInfo + + @eth OMG.Eth.zero_address() + + describe "get_inputs/1" do + test "returns list of input utxos" do + inputs1 = [ + Position.encode({:utxo_position, 1, 0, 0}), + Position.encode({:utxo_position, 1, 0, 1}) + ] + + inputs2 = [Position.encode({:utxo_position, 1, 0, 2})] + + ife_infos = [ + ife_info_with_inputs(inputs1), + ife_info_with_inputs(inputs2) + ] + + expected = inputs1 ++ inputs2 + + assert InFlightExitInfo.get_inputs(ife_infos) == expected + end + end + + defp ife_info_with_inputs(inputs) do + tx = + Transaction.Payment.new( + [{1, 0, 0}], + [{"alice", @eth, 1}, {"alice", @eth, 2}], + <<0::256>> + ) + + %InFlightExitInfo{ + tx: %Transaction.Signed{raw_tx: tx, sigs: <<1::520>>}, + timestamp: 1, + contract_id: <<1::160>>, + eth_height: 1, + is_active: true, + input_utxos_pos: inputs + } + end +end diff --git a/priv/cabbage b/priv/cabbage index 96d67f4e06..cd262a3b62 160000 --- a/priv/cabbage +++ b/priv/cabbage @@ -1 +1 @@ -Subproject commit 96d67f4e065aad020dfde111419e95e397f443e8 +Subproject commit cd262a3b624a347b05208b154e0c2d7f85dd92a9