diff --git a/lib/network/model/connection.ex b/lib/network/model/connection.ex index 7d900bba..666e71c2 100644 --- a/lib/network/model/connection.ex +++ b/lib/network/model/connection.ex @@ -27,6 +27,7 @@ defmodule Helix.Network.Model.Connection do @type public_ftp :: t_of_type(:public_ftp) @type bank_login :: t_of_type(:bank_login) @type wire_transfer :: t_of_type(:wire_transfer) + @type virus_collect :: t_of_type(:virus_collect) @type cracker_bruteforce :: t_of_type(:cracker_bruteforce) @type meta :: map | nil @@ -37,6 +38,7 @@ defmodule Helix.Network.Model.Connection do | :public_ftp | :bank_login | :wire_transfer + | :virus_collect | :cracker_bruteforce @type close_reasons :: :normal | :force diff --git a/lib/process/executable.ex b/lib/process/executable.ex index 81875e1d..6f0c274b 100644 --- a/lib/process/executable.ex +++ b/lib/process/executable.ex @@ -274,6 +274,10 @@ defmodule Helix.Process.Executable do # Defaults: in case these functions were not defined, we assume the # process is not interested on this (optional) data. + + @spec get_bounce_id(Server.t, Server.t, params, meta) :: + %{bounce_id: Bounce.id | nil} + @doc false defp get_bounce_id(_, _, _, %{bounce: bounce = %Bounce{}}), do: %{bounce_id: bounce.bounce_id} defp get_bounce_id(_, _, _, %{bounce: bounce_id = %Bounce.ID{}}), @@ -281,24 +285,54 @@ defmodule Helix.Process.Executable do defp get_bounce_id(_, _, _, _), do: %{bounce_id: nil} + @spec get_source_connection(Server.t, Server.t, params, meta) :: + {:create, Connection.type} + | nil + @doc false defp get_source_connection(_, _, _, _), do: nil + @spec get_target_connection(Server.t, Server.t, params, meta) :: + {:create, Connection.type} + | :same_origin + | nil + @doc false defp get_target_connection(_, _, _, _), do: nil + @spec get_source_file(Server.t, Server.t, params, meta) :: + %{src_file_id: File.id | nil} + @doc false defp get_source_file(_, _, _, _), do: %{src_file_id: nil} + @spec get_target_file(Server.t, Server.t, params, meta) :: + %{tgt_file_id: File.id | nil} + @doc false defp get_target_file(_, _, _, _), do: %{tgt_file_id: nil} + @spec get_source_bank_account(Server.t, Server.t, params, meta) :: + %{ + src_atm_id: Server.t | nil, + src_acc_number: BankAccount.account | nil + } + @doc false defp get_source_bank_account(_, _, _, _), do: %{src_atm_id: nil, src_acc_number: nil} + @spec get_target_bank_account(Server.t, Server.t, params, meta) :: + %{ + tgt_atm_id: Server.t | nil, + tgt_acc_number: BankAccount.account | nil + } + @doc false defp get_target_bank_account(_, _, _, _), do: %{tgt_atm_id: nil, tgt_acc_number: nil} + @spec get_target_process(Server.t, Server.t, params, meta) :: + %{tgt_process_id: Process.t | nil} + @doc false defp get_target_process(_, _, _, _), do: %{tgt_process_id: nil} end @@ -406,10 +440,6 @@ defmodule Helix.Process.Executable do quote do - @spec get_source_connection(term, term, term, term) :: - {:create, Connection.type} - | nil - @doc false defp get_source_connection(unquote_splicing(args)) do unquote(block) end @@ -431,11 +461,6 @@ defmodule Helix.Process.Executable do quote do - @spec get_target_connection(term, term, term, term) :: - {:create, Connection.type} - | :same_origin - | nil - @doc false defp get_target_connection(unquote_splicing(args)) do unquote(block) end @@ -452,9 +477,6 @@ defmodule Helix.Process.Executable do quote do - @spec get_source_file(term, term, term, term) :: - %{src_file_id: File.id | nil} - @doc false defp get_source_file(unquote_splicing(args)) do file_id = unquote(block) @@ -473,9 +495,6 @@ defmodule Helix.Process.Executable do quote do - @spec get_target_file(term, term, term, term) :: - %{tgt_file_id: File.id | nil} - @doc false defp get_target_file(unquote_splicing(args)) do file_id = unquote(block) @@ -494,14 +513,10 @@ defmodule Helix.Process.Executable do quote do - @spec get_source_bank_account(term, term, term, term) :: - %{ - src_atm_id: Server.t | nil, - src_acc_number: BankAccount.account | nil - } - @doc false defp get_source_bank_account(unquote_splicing(args)) do - {atm_id, account_number} = unquote(block) + {atm_id, account_number} = + unquote(block) + |> get_bank_data() %{ src_atm_id: atm_id, @@ -521,14 +536,10 @@ defmodule Helix.Process.Executable do quote do - @spec get_target_bank_account(term, term, term, term) :: - %{ - tgt_atm_id: Server.t | nil, - tgt_acc_number: BankAccount.account | nil - } - @doc false defp get_target_bank_account(unquote_splicing(args)) do - {atm_id, account_number} = unquote(block) + {atm_id, account_number} = + unquote(block) + |> get_bank_data() %{ tgt_atm_id: atm_id, @@ -539,6 +550,16 @@ defmodule Helix.Process.Executable do end end + @spec get_bank_data(BankAccount.t) :: {Server.id, BankAccount.account} + @spec get_bank_data(tuple) :: tuple + @spec get_bank_data(nil) :: {nil, nil} + def get_bank_data(%BankAccount{atm_id: atm_id, account_number: number}), + do: {atm_id, number} + def get_bank_data(result = {_, _}), + do: result + def get_bank_data(nil), + do: {nil, nil} + @doc """ Returns the process' `tgt_process_id`, as defined on the `target_process` section of the Process.Executable. @@ -548,9 +569,6 @@ defmodule Helix.Process.Executable do quote do - @spec get_target_process(term, term, term, term) :: - %{tgt_process_id: Process.t | nil} - @doc false defp get_target_process(unquote_splicing(args)) do process_id = unquote(block) @@ -573,7 +591,7 @@ defmodule Helix.Process.Executable do quote do - @spec get_resources(term, term, term, term) :: + @spec get_resources(Server.t, Server.t, params, meta) :: unquote(process).resources @doc false defp get_resources(unquote_splicing(args)) do diff --git a/lib/software/action/flow/virus.ex b/lib/software/action/flow/virus.ex new file mode 100644 index 00000000..74550d52 --- /dev/null +++ b/lib/software/action/flow/virus.ex @@ -0,0 +1,44 @@ +defmodule Helix.Software.Action.Flow.Virus do + + alias Helix.Event + alias Helix.Network.Model.Tunnel + alias Helix.Network.Query.Network, as: NetworkQuery + alias Helix.Process.Model.Process + alias Helix.Server.Model.Server + alias Helix.Software.Model.File + alias Helix.Software.Model.Virus + + alias Helix.Software.Process.Virus.Collect, as: VirusCollectProcess + + @internet_id NetworkQuery.internet().network_id + + @type viruses :: [{File.t, Server.t}] + + @spec start_collect( + Server.t, viruses, Tunnel.bounce_id, Virus.payment_info, Event.relay) + :: + [Process.t] + | no_return + def start_collect(gateway, viruses, bounce_id, {bank_acc, wallet}, relay) do + Enum.reduce(viruses, [], fn {virus, target}, acc -> + + params = + %{ + wallet: wallet, + bank_account: bank_acc + } + + meta = + %{ + virus: virus, + network_id: @internet_id, + bounce: bounce_id + } + + {:ok, process} = + VirusCollectProcess.execute(gateway, target, params, meta, relay) + + acc ++ [process] + end) + end +end diff --git a/lib/software/event/virus/collect.ex b/lib/software/event/virus/collect.ex new file mode 100644 index 00000000..553bd0b3 --- /dev/null +++ b/lib/software/event/virus/collect.ex @@ -0,0 +1,42 @@ +defmodule Helix.Software.Event.Virus.Collect do + + import Helix.Event + + event Processed do + + alias Helix.Process.Model.Process + alias Helix.Universe.Bank.Model.BankAccount + alias Helix.Universe.Bank.Query.Bank, as: BankQuery + alias Helix.Software.Model.File + alias Helix.Software.Query.File, as: FileQuery + + alias Helix.Software.Process.Virus.Collect, as: VirusCollectProcess + + event_struct [:file, :bank_account, :wallet] + + @type t :: + %__MODULE__{ + file: File.t, + bank_account: BankAccount.t | nil, + wallet: term | nil + } + + def new(process = %Process{}, %VirusCollectProcess{wallet: nil}) do + bank_account = + BankQuery.fetch_account(process.src_atm_id, process.src_acc_number) + + do_new(process, bank_account, nil) + end + + def new(process = %Process{}, %VirusCollectProcess{wallet: wallet}), + do: do_new(process, nil, wallet) + + defp do_new(process, bank_account, wallet) do + %__MODULE__{ + file: FileQuery.fetch(process.src_file_id), + bank_account: bank_account, + wallet: wallet + } + end + end +end diff --git a/lib/software/henforcer/virus.ex b/lib/software/henforcer/virus.ex index 31377046..7ceac50c 100644 --- a/lib/software/henforcer/virus.ex +++ b/lib/software/henforcer/virus.ex @@ -4,7 +4,6 @@ defmodule Helix.Software.Henforcer.Virus do alias Helix.Entity.Henforcer.Entity, as: EntityHenforcer alias Helix.Entity.Model.Entity - alias Helix.Universe.Bank.Model.BankAccount alias Helix.Software.Henforcer.File, as: FileHenforcer alias Helix.Software.Henforcer.Storage, as: StorageHenforcer alias Helix.Software.Model.File @@ -12,8 +11,6 @@ defmodule Helix.Software.Henforcer.Virus do alias Helix.Software.Model.Virus alias Helix.Software.Query.Virus, as: VirusQuery - @type payment_info :: {BankAccount.t | nil, term | nil} - @type virus_exists_relay :: %{virus: Virus.t} @type virus_exists_relay_partial :: %{} @type virus_exists_error :: @@ -72,7 +69,7 @@ defmodule Helix.Software.Henforcer.Virus do @type can_collect_all_relay_partial :: map @type can_collect_all_error :: can_collect_error - @spec can_collect_all?(Entity.t, [File.id], payment_info) :: + @spec can_collect_all?(Entity.t, [File.id], Virus.payment_info) :: {true, can_collect_all_relay} | can_collect_all_error @doc """ @@ -105,7 +102,7 @@ defmodule Helix.Software.Henforcer.Virus do | is_active_error | valid_payment_error - @spec can_collect?(Entity.t, File.id, payment_info) :: + @spec can_collect?(Entity.t, File.id, Virus.payment_info) :: {true, can_collect_relay} | can_collect_error @doc """ @@ -133,7 +130,7 @@ defmodule Helix.Software.Henforcer.Virus do @type valid_payment_error :: {false, {:payment, :invalid}, valid_payment_relay_partial} - @spec valid_payment?(File.t, payment_info) :: + @spec valid_payment?(File.t, Virus.payment_info) :: {true, valid_payment_relay} | valid_payment_error @doc """ diff --git a/lib/software/model/virus.ex b/lib/software/model/virus.ex index a73ff9e0..d5df1b2b 100644 --- a/lib/software/model/virus.ex +++ b/lib/software/model/virus.ex @@ -11,6 +11,7 @@ defmodule Helix.Software.Model.Virus do alias Ecto.Changeset alias Helix.Entity.Model.Entity + alias Helix.Universe.Bank.Model.BankAccount alias Helix.Software.Model.File alias __MODULE__, as: Virus @@ -21,6 +22,13 @@ defmodule Helix.Software.Model.Virus do is_active?: boolean } + @typep wallet :: term + + @type payment_info :: + {BankAccount.t, wallet} + | {nil, wallet} + | {BankAccount.t, nil} + @type changeset :: %Changeset{data: %__MODULE__{}} @type id :: File.id diff --git a/lib/software/process/file/transfer.ex b/lib/software/process/file/transfer.ex index 592f7c37..54c9301a 100644 --- a/lib/software/process/file/transfer.ex +++ b/lib/software/process/file/transfer.ex @@ -35,6 +35,7 @@ process Helix.Software.Process.File.Transfer do %{dlk: resource_usage} | %{ulk: resource_usage} + @type process_type :: :file_download | :file_upload @type transfer_type :: :download | :upload @type connection_type :: :ftp | :public_ftp @@ -197,15 +198,20 @@ process Helix.Software.Process.File.Transfer do Defines how FileTransferProcess should be executed. """ + alias Helix.Network.Model.Bounce + alias Helix.Network.Model.Network alias Helix.Software.Model.File alias Helix.Software.Process.File.Transfer, as: FileTransferProcess @type params :: FileTransferProcess.creation_params - @type meta :: %{ - :file => File.t, - optional(atom) => term - } + @type meta :: + %{ + file: File.t, + type: FileTransferProcess.process_type, + network_id: Network.id, + bounce: Bounce.idt | nil + } resources(_, _, params, meta) do %{ diff --git a/lib/software/process/virus/collect.ex b/lib/software/process/virus/collect.ex new file mode 100644 index 00000000..393315e6 --- /dev/null +++ b/lib/software/process/virus/collect.ex @@ -0,0 +1,128 @@ +import Helix.Process + +process Helix.Software.Process.Virus.Collect do + + alias Helix.Universe.Bank.Model.BankAccount + + process_struct [:wallet] + + @process_type :virus_collect + + @type t :: + %__MODULE__{ + wallet: term + } + + @type resources :: + %{ + objective: objective, + l_dynamic: [:cpu], + r_dynamic: [], + static: map + } + + @type objective :: + %{cpu: resource_usage} + + @type creation_params :: + %{ + wallet: term | nil, + bank_account: BankAccount.t | nil + } + + @type resources_params :: map + + @spec new(creation_params) :: + t + def new(%{wallet: wallet}) do + %__MODULE__{ + wallet: wallet + } + end + + @spec resources(resources_params) :: + resources + def resources(params), + do: get_resources params + + processable do + on_kill(_process, _data, _reason) do + {:delete, []} + end + + on_completion(_process, _data) do + {:delete, []} + end + end + + resourceable do + + alias Helix.Software.Process.Virus.Collect, as: VirusCollectProcess + + @type params :: VirusCollectProcess.resources_params + + @type factors :: map + + get_factors(_params) do + end + + cpu(_) do + 500 + end + + static do + %{ + paused: %{ram: 10}, + running: %{ram: 20} + } + end + + dynamic do + [:cpu] + end + end + + executable do + + alias Helix.Network.Model.Bounce + alias Helix.Network.Model.Network + alias Helix.Software.Model.File + alias Helix.Software.Process.Virus.Collect, as: VirusCollectProcess + + @type params :: VirusCollectProcess.creation_params + + @type meta :: + %{ + virus: File.t, + network_id: Network.id, + bounce: Bounce.idt | nil + } + + resources(_, _, _params, _meta) do + %{} + end + + source_file(_, _, _, %{virus: virus}) do + virus.file_id + end + + source_connection(_, _, _, _) do + {:create, :virus_collect} + end + + target_bank_account(_, _, _, %{virus: %{software_type: :virus_miner}}) do + nil + end + + target_bank_account(_, _, %{bank_account: bank_acc}, _) do + bank_acc + end + end + + process_viewable do + + @type data :: %{} + + render_empty_data() + end +end diff --git a/lib/universe/bank/internal/bank_account.ex b/lib/universe/bank/internal/bank_account.ex index b8c06449..f32d3e56 100644 --- a/lib/universe/bank/internal/bank_account.ex +++ b/lib/universe/bank/internal/bank_account.ex @@ -23,9 +23,8 @@ defmodule Helix.Universe.Bank.Internal.BankAccount do a transaction. """ def fetch_for_update(atm, account_number) do - unless Repo.in_transaction?() do - raise "Transaction required in order to acquiring lock" - end + unless Repo.in_transaction?(), + do: raise "Transaction required in order to acquiring lock" atm |> BankAccount.Query.by_atm_account(account_number) diff --git a/lib/universe/bank/internal/bank_transfer.ex b/lib/universe/bank/internal/bank_transfer.ex index 5fdf6944..230e007e 100644 --- a/lib/universe/bank/internal/bank_transfer.ex +++ b/lib/universe/bank/internal/bank_transfer.ex @@ -65,11 +65,9 @@ defmodule Helix.Universe.Bank.Internal.BankTransfer do | {:error, :internal} def complete(transfer) do deposit_money = fn(transfer) -> - account_to = BankAccountInternal.fetch_for_update( - transfer.atm_to, - transfer.account_to) - - BankAccountInternal.deposit(account_to, transfer.amount) + transfer.atm_to + |> BankAccountInternal.fetch_for_update(transfer.account_to) + |> BankAccountInternal.deposit(transfer.amount) end trans = @@ -106,12 +104,10 @@ defmodule Helix.Universe.Bank.Internal.BankTransfer do | {:error, {:transfer, :notfound}} | {:error, :internal} def abort(transfer) do - refund_money = fn(transfer) -> - account_from = BankAccountInternal.fetch_for_update( - transfer.atm_from, - transfer.account_from) - - BankAccountInternal.deposit(account_from, transfer.amount) + refund_money = fn transfer -> + transfer.atm_from + |> BankAccountInternal.fetch_for_update(transfer.account_from) + |> BankAccountInternal.deposit(transfer.amount) end trans = diff --git a/test/software/action/flow/virus_test.exs b/test/software/action/flow/virus_test.exs new file mode 100644 index 00000000..65ce5e53 --- /dev/null +++ b/test/software/action/flow/virus_test.exs @@ -0,0 +1,101 @@ +defmodule Helix.Software.Action.Flow.VirusTest do + + use Helix.Test.Case.Integration + + alias Helix.Network.Query.Tunnel, as: TunnelQuery + alias Helix.Software.Action.Flow.Virus, as: VirusFlow + + alias Helix.Test.Network.Setup, as: NetworkSetup + alias Helix.Test.Process.TOPHelper + alias Helix.Test.Server.Setup, as: ServerSetup + alias Helix.Test.Universe.Bank.Setup, as: BankSetup + alias Helix.Test.Software.Helper, as: SoftwareHelper + alias Helix.Test.Software.Setup, as: SoftwareSetup + + @relay nil + + describe "start_collect/5" do + test "starts virus collect" do + {gateway, %{entity: entity}} = ServerSetup.server() + + {target1, _} = ServerSetup.server() + {target2, _} = ServerSetup.server() + + storage1_id = SoftwareHelper.get_storage_id(target1) + storage2_id = SoftwareHelper.get_storage_id(target2) + + {_virus1, %{file: file1}} = + SoftwareSetup.Virus.virus( + entity_id: entity.entity_id, + storage_id: storage1_id, + real_file?: true + ) + + # use a `miner` once available (#244) + {_virus2, %{file: file2}} = + SoftwareSetup.Virus.virus( + entity_id: entity.entity_id, + storage_id: storage2_id, + real_file?: true + ) + + viruses = [{file1, target1}, {file2, target2}] + + {bank_acc, _} = BankSetup.fake_account() + wallet = nil + + {bounce, _} = NetworkSetup.Bounce.bounce() + + assert [process1, process2] = + VirusFlow.start_collect( + gateway, viruses, bounce.bounce_id, {bank_acc, wallet}, @relay + ) + + # Collect of file1: + assert process1.gateway_id == gateway.server_id + assert process1.target_id == target1.server_id + assert process1.source_entity_id == entity.entity_id + assert process1.src_connection_id + assert process1.src_file_id == file1.file_id + assert process1.bounce_id == bounce.bounce_id + assert process1.tgt_atm_id == bank_acc.atm_id + assert process1.tgt_acc_number == bank_acc.account_number + refute process1.data.wallet + + # Collect of file2: + assert process2.gateway_id == gateway.server_id + assert process2.target_id == target2.server_id + assert process2.source_entity_id == entity.entity_id + assert process2.src_connection_id + assert process2.src_file_id == file2.file_id + assert process2.bounce_id == bounce.bounce_id + assert process2.tgt_atm_id == bank_acc.atm_id + assert process2.tgt_acc_number == bank_acc.account_number + refute process2.data.wallet + + # Make sure the connections were created as well + connection1 = TunnelQuery.fetch_connection(process1.src_connection_id) + tunnel1 = TunnelQuery.fetch_from_connection(connection1) + + # Connection has the right type + assert connection1.connection_type == :virus_collect + + # Underlying tunnel was created and it only contains `virus_collect` conn + assert tunnel1.bounce == bounce + assert [connection1] == TunnelQuery.get_connections(tunnel1) + + # Same for `file2`... + connection2 = TunnelQuery.fetch_connection(process2.src_connection_id) + tunnel2 = TunnelQuery.fetch_from_connection(connection2) + + # Connection has the right type + assert connection2.connection_type == :virus_collect + + # Underlying tunnel was created and it only contains `virus_collect` conn + assert tunnel2.bounce == bounce + assert [connection2] == TunnelQuery.get_connections(tunnel2) + + TOPHelper.top_stop() + end + end +end diff --git a/test/support/network/bounce/setup.ex b/test/support/network/bounce/setup.ex index 6d9ec1b7..f80d0d55 100644 --- a/test/support/network/bounce/setup.ex +++ b/test/support/network/bounce/setup.ex @@ -77,7 +77,8 @@ defmodule Helix.Test.Network.Setup.Bounce do name: name, entity_id: entity_id, sorted: sorted - } |> Changeset.change() + } + |> Changeset.change() bounce = changeset