From 8e64c1a76c1ca546fc2291f5c0505c3bd41307e3 Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Wed, 29 Mar 2023 16:28:14 +0200 Subject: [PATCH 1/5] remove get_calls from legacy --- .../interpreter/legacy/action_interpreter.ex | 28 ------- .../contracts/interpreter/legacy/library.ex | 17 ---- .../contracts/interpreter/library/contract.ex | 7 +- .../legacy/action_interpreter_test.exs | 83 ------------------- 4 files changed, 4 insertions(+), 131 deletions(-) diff --git a/lib/archethic/contracts/interpreter/legacy/action_interpreter.ex b/lib/archethic/contracts/interpreter/legacy/action_interpreter.ex index a32192358..08b8dbaab 100644 --- a/lib/archethic/contracts/interpreter/legacy/action_interpreter.ex +++ b/lib/archethic/contracts/interpreter/legacy/action_interpreter.ex @@ -139,14 +139,6 @@ defmodule Archethic.Contracts.Interpreter.Legacy.ActionInterpreter do {node, {:ok, %{context | scope: {:function, function, parent_scope}}}} end - # Whitelist the get_calls/0 (this will be expanded to a get_calls(contract.address) in postwalk) - defp prewalk( - node = {{:atom, "get_calls"}, _, []}, - acc = {:ok, %{scope: {:actions, _}}} - ) do - {node, acc} - end - # Blacklist the add_uco_transfer argument list defp prewalk( node = {{:atom, "to"}, address}, @@ -294,26 +286,6 @@ defmodule Archethic.Contracts.Interpreter.Legacy.ActionInterpreter do end end - # expand get_calls() to get_calls(contract.address) - defp postwalk( - {{:atom, "get_calls"}, meta, []}, - acc - ) do - node = { - {:atom, "get_calls"}, - meta, - [ - {:get_in, meta, - [ - {:scope, meta, nil}, - ["contract", "address"] - ]} - ] - } - - {node, acc} - end - defp postwalk(node, acc) do UtilsInterpreter.postwalk(node, acc) end diff --git a/lib/archethic/contracts/interpreter/legacy/library.ex b/lib/archethic/contracts/interpreter/legacy/library.ex index 9dee72ab7..e1fa98ade 100644 --- a/lib/archethic/contracts/interpreter/legacy/library.ex +++ b/lib/archethic/contracts/interpreter/legacy/library.ex @@ -190,23 +190,6 @@ defmodule Archethic.Contracts.Interpreter.Legacy.Library do def size(list) when is_list(list), do: length(list) def size(map) when is_map(map), do: map_size(map) - @doc """ - Get the inputs(type= :call) of the given transaction - - This is useful for contracts that want to throttle their calls - """ - @spec get_calls(binary()) :: list(map()) - def get_calls(contract_address) do - contract_address - |> UtilsInterpreter.maybe_decode_hex() - |> TransactionLookup.list_contract_transactions() - |> Enum.map(fn {address, _, _} -> - # TODO: parallelize - {:ok, tx} = TransactionChain.get_transaction(address, [], :io) - ContractConstants.from_transaction(tx) - end) - end - @doc """ Get the genesis public key """ diff --git a/lib/archethic/contracts/interpreter/library/contract.ex b/lib/archethic/contracts/interpreter/library/contract.ex index f52c93109..830d6fd8b 100644 --- a/lib/archethic/contracts/interpreter/library/contract.ex +++ b/lib/archethic/contracts/interpreter/library/contract.ex @@ -8,14 +8,15 @@ defmodule Archethic.Contracts.Interpreter.Library.Contract do alias Archethic.Contracts.Interpreter.ASTHelper, as: AST alias Archethic.TransactionChain.Transaction - alias Archethic.Contracts.Interpreter.Legacy alias Archethic.Contracts.Interpreter.Legacy.TransactionStatements # get_calls has it's own postwalk (to inject the address), # it does not require a check_types @spec get_calls(binary()) :: list(map()) - defdelegate get_calls(contract_address), - to: Legacy.Library + def get_calls(contract_address) do + # FIXME TODO + [] + end @spec set_type(Transaction.t(), binary()) :: Transaction.t() defdelegate set_type(next_tx, type), diff --git a/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs b/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs index d47278c26..77e351cf0 100644 --- a/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs +++ b/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs @@ -322,32 +322,6 @@ defmodule Archethic.Contracts.Interpreter.Legacy.ActionInterpreterTest do |> ActionInterpreter.execute() end - test "get_calls() should be expanded to get_calls(contract.address)" do - {:ok, :transaction, ast} = - ~S""" - actions triggered_by: transaction do - get_calls() - end - """ - |> Interpreter.sanitize_code() - |> elem(1) - |> ActionInterpreter.parse() - - assert {{:., [line: 2], - [ - {:__aliases__, [alias: Archethic.Contracts.Interpreter.Legacy.Library], - [:Library]}, - :get_calls - ]}, [line: 2], - [ - {:get_in, meta, - [ - {:scope, meta, nil}, - ["contract", "address"] - ]} - ]} = ast - end - test "shall use get_genesis_address/1 in actions" do key = <<0::16, :crypto.strong_rand_bytes(32)::binary>> @@ -481,63 +455,6 @@ defmodule Archethic.Contracts.Interpreter.Legacy.ActionInterpreterTest do |> ActionInterpreter.execute() end - test "shall use get_calls/1 in actions" do - key = <<0::16, :crypto.strong_rand_bytes(32)::binary>> - - P2P.add_and_connect_node(%Node{ - ip: {127, 0, 0, 1}, - port: 3000, - first_public_key: key, - last_public_key: key, - available?: true, - geo_patch: "AAA", - network_patch: "AAA", - authorized?: true, - authorization_date: DateTime.utc_now() - }) - - address = Base.decode16!("64F05F5236088FC64D1BB19BD13BC548F1C49A42432AF02AD9024D8A2990B2B4") - - MockDB - |> stub(:get_inputs, fn _, ^address -> - [ - %VersionedTransactionInput{ - protocol_version: ArchethicCase.current_protocol_version(), - input: %TransactionInput{ - from: address, - amount: nil, - type: :call, - timestamp: DateTime.utc_now(), - spent?: false, - reward?: false - } - } - ] - end) - |> stub(:get_transaction, fn ^address, _, _ -> - {:ok, TransactionFactory.create_valid_transaction()} - end) - - # TODO: find a way to check the transactions returned by get_calls - - assert %Transaction{data: %TransactionData{content: "1"}} = - ~s""" - actions triggered_by: transaction do - transactions = get_calls() - set_content size(transactions) - end - """ - |> Interpreter.sanitize_code() - |> elem(1) - |> ActionInterpreter.parse() - |> elem(2) - |> ActionInterpreter.execute(%{ - "contract" => %{ - "address" => "64F05F5236088FC64D1BB19BD13BC548F1C49A42432AF02AD9024D8A2990B2B4" - } - }) - end - describe "blacklist" do test "should parse when arguments are allowed" do assert {:ok, :transaction, _ast} = From ba3a6b40d95a25ee29865330c5c87d957cf30e0c Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Thu, 30 Mar 2023 16:29:09 +0200 Subject: [PATCH 2/5] GetContractCalls message & pass the calls to the Interpreter.execute --- lib/archethic.ex | 4 +- lib/archethic/contracts/interpreter.ex | 36 +++++------ .../interpreter/action_interpreter.ex | 11 ++-- .../interpreter/condition_interpreter.ex | 18 ------ .../contracts/interpreter/legacy/library.ex | 2 - .../contracts/interpreter/library/contract.ex | 16 +++-- lib/archethic/contracts/worker.ex | 10 +-- .../p2p/message/get_contract_calls.ex | 43 +++++++++++++ lib/archethic/p2p/message/message_id.ex | 4 +- lib/archethic/transaction_chain.ex | 25 ++++++++ .../controllers/api/transaction_controller.ex | 2 +- .../legacy/action_interpreter_test.exs | 4 -- .../interpreter/library/contract_test.exs | 34 ++++------- test/archethic/contracts/interpreter_test.exs | 18 +++--- test/archethic/contracts/worker_test.exs | 31 +++++----- .../p2p/message/get_contract_calls_test.exs | 61 +++++++++++++++++++ 16 files changed, 208 insertions(+), 111 deletions(-) create mode 100644 lib/archethic/p2p/message/get_contract_calls.ex create mode 100644 test/archethic/p2p/message/get_contract_calls_test.exs diff --git a/lib/archethic.ex b/lib/archethic.ex index 01befa55d..19db047d1 100644 --- a/lib/archethic.ex +++ b/lib/archethic.ex @@ -323,7 +323,7 @@ defmodule Archethic do @spec execute_contract( Contract.trigger_type(), Contract.t(), - nil | Transaction.t() + [Transaction.t()] ) :: {:ok, nil | Transaction.t()} | {:error, @@ -332,7 +332,7 @@ defmodule Archethic do | :invalid_transaction_constraints | :invalid_oracle_constraints | :invalid_inherit_constraints} - defdelegate execute_contract(trigger_type, contract, maybe_tx), + defdelegate execute_contract(trigger_type, contract, calls), to: Interpreter, as: :execute diff --git a/lib/archethic/contracts/interpreter.ex b/lib/archethic/contracts/interpreter.ex index aa9794ab2..307f55830 100644 --- a/lib/archethic/contracts/interpreter.ex +++ b/lib/archethic/contracts/interpreter.ex @@ -111,17 +111,12 @@ defmodule Archethic.Contracts.Interpreter do Execution the given contract's trigger. Checks all conditions block. - `maybe_tx` must be - - the incoming transaction when trigger_type: transaction - - the oracle transaction when trigger_type: oracle - - nil for the other trigger_types - /!\ The transaction returned is not complete, only the `type` and `data` are filled-in. """ @spec execute( Contract.trigger_type(), Contract.t(), - nil | Transaction.t(), + [Transaction.t()], execute_opts() ) :: {:ok, nil | Transaction.t()} @@ -134,7 +129,7 @@ defmodule Archethic.Contracts.Interpreter do def execute( trigger_type, contract = %Contract{triggers: triggers}, - maybe_tx, + calls, opts \\ [] ) do case triggers[trigger_type] do @@ -146,7 +141,7 @@ defmodule Archethic.Contracts.Interpreter do trigger_type, trigger_code, contract, - maybe_tx, + calls, contract, opts ) @@ -222,7 +217,7 @@ defmodule Archethic.Contracts.Interpreter do :transaction, trigger_code, contract, - incoming_tx = %Transaction{}, + calls = [incoming_tx = %Transaction{}], %Contract{ version: version, conditions: conditions, @@ -238,7 +233,7 @@ defmodule Archethic.Contracts.Interpreter do } if valid_conditions?(version, conditions.transaction, constants) do - case execute_trigger(version, trigger_code, contract, incoming_tx) do + case execute_trigger(version, trigger_code, contract, calls) do nil -> {:ok, nil} @@ -262,7 +257,7 @@ defmodule Archethic.Contracts.Interpreter do :oracle, trigger_code, contract, - oracle_tx = %Transaction{}, + calls = [oracle_tx = %Transaction{}], %Contract{ version: version, conditions: conditions, @@ -278,7 +273,7 @@ defmodule Archethic.Contracts.Interpreter do } if valid_conditions?(version, conditions.oracle, constants) do - case execute_trigger(version, trigger_code, contract, oracle_tx) do + case execute_trigger(version, trigger_code, contract, calls) do nil -> {:ok, nil} @@ -298,11 +293,11 @@ defmodule Archethic.Contracts.Interpreter do _trigger_type, trigger_code, contract, - nil, + calls, %Contract{version: version}, opts ) do - case execute_trigger(version, trigger_code, contract) do + case execute_trigger(version, trigger_code, contract, calls) do nil -> {:ok, nil} @@ -319,13 +314,18 @@ defmodule Archethic.Contracts.Interpreter do version, trigger_code, contract, - maybe_tx \\ nil + calls ) do constants_trigger = %{ + "calls" => Enum.map(calls, &Constants.from_transaction/1), "transaction" => - case maybe_tx do - nil -> nil - tx -> Constants.from_transaction(tx) + case calls do + [tx] -> + # :oracle & :transaction + Constants.from_transaction(tx) + + _ -> + nil end, "contract" => contract.constants.contract } diff --git a/lib/archethic/contracts/interpreter/action_interpreter.ex b/lib/archethic/contracts/interpreter/action_interpreter.ex index 7175a187d..01fe2cf73 100644 --- a/lib/archethic/contracts/interpreter/action_interpreter.ex +++ b/lib/archethic/contracts/interpreter/action_interpreter.ex @@ -47,8 +47,9 @@ defmodule Archethic.Contracts.Interpreter.ActionInterpreter do # because it is mutable. # # constants should already contains the global variables: + # - "calls": the transactions that called this exact contract version # - "contract": current contract transaction - # - "transaction": the incoming transaction (when trigger=transaction) + # - "transaction": the incoming transaction (when trigger=transaction|oracle) Scope.init(Map.put(constants, "next_transaction", next_tx)) # we can ignore the result & binding @@ -150,19 +151,15 @@ defmodule Archethic.Contracts.Interpreter.ActionInterpreter do # | .__/ \___/|___/\__| \_/\_/ \__,_|_|_|\_\ # |_| # ---------------------------------------------------------------------- - # Contract.get_calls() => Contract.get_calls(contract.address) + # Contract.get_calls() defp postwalk( _node = {{:., _meta, [{:__aliases__, _, [atom: "Contract"]}, {:atom, "get_calls"}]}, _, []}, acc ) do - # contract is one of the "magic" variables that we expose to the user's code - # it is bound in the root scope new_node = quote do - Archethic.Contracts.Interpreter.Library.Contract.get_calls( - Scope.read_global(["contract", "address"]) - ) + Archethic.Contracts.Interpreter.Library.Contract.get_calls() end {new_node, acc} diff --git a/lib/archethic/contracts/interpreter/condition_interpreter.ex b/lib/archethic/contracts/interpreter/condition_interpreter.ex index f6114a566..48cd20640 100644 --- a/lib/archethic/contracts/interpreter/condition_interpreter.ex +++ b/lib/archethic/contracts/interpreter/condition_interpreter.ex @@ -171,24 +171,6 @@ defmodule Archethic.Contracts.Interpreter.ConditionInterpreter do {new_node, acc} end - # Override Contract.get_calls() - defp postwalk( - _subject = [_global_variable, _], - node = - {{:., _meta, [{:__aliases__, _, [atom: "Contract"]}, {:atom, "get_calls"}]}, _, []}, - _acc - ) do - # new_node = - # quote do - # Archethic.Contracts.Interpreter.Library.Contract.get_calls( - # Scope.read_global([unquote(global_variable), "address"]) - # ) - # end - - # {new_node, acc} - throw({:error, node, "Contract.get_calls() not yet implemented in the conditions"}) - end - defp postwalk(_subject, node, acc) do CommonInterpreter.postwalk(node, acc) end diff --git a/lib/archethic/contracts/interpreter/legacy/library.ex b/lib/archethic/contracts/interpreter/legacy/library.ex index e1fa98ade..1a048f652 100644 --- a/lib/archethic/contracts/interpreter/legacy/library.ex +++ b/lib/archethic/contracts/interpreter/legacy/library.ex @@ -7,8 +7,6 @@ defmodule Archethic.Contracts.Interpreter.Legacy.Library do P2P.Message.GetFirstPublicKey, P2P.Message.FirstPublicKey, TransactionChain, - Contracts.ContractConstants, - Contracts.TransactionLookup, Utils } diff --git a/lib/archethic/contracts/interpreter/library/contract.ex b/lib/archethic/contracts/interpreter/library/contract.ex index 830d6fd8b..23676b62c 100644 --- a/lib/archethic/contracts/interpreter/library/contract.ex +++ b/lib/archethic/contracts/interpreter/library/contract.ex @@ -6,16 +6,16 @@ defmodule Archethic.Contracts.Interpreter.Library.Contract do """ @behaviour Archethic.Contracts.Interpreter.Library + alias Archethic.Contracts.Interpreter.Scope alias Archethic.Contracts.Interpreter.ASTHelper, as: AST alias Archethic.TransactionChain.Transaction alias Archethic.Contracts.Interpreter.Legacy.TransactionStatements - # get_calls has it's own postwalk (to inject the address), - # it does not require a check_types - @spec get_calls(binary()) :: list(map()) - def get_calls(contract_address) do - # FIXME TODO - [] + @spec get_calls() :: list(map()) + def get_calls() do + # DISCUSS: + # this function is not really needed, we might just tell the users there is a "calls" global variable? + Scope.read_global(["calls"]) end @spec set_type(Transaction.t(), binary()) :: Transaction.t() @@ -121,5 +121,9 @@ defmodule Archethic.Contracts.Interpreter.Library.Contract do AST.is_list?(first) || AST.is_variable_or_function_call?(first) end + def check_types(:get_calls, []) do + true + end + def check_types(_, _), do: false end diff --git a/lib/archethic/contracts/worker.ex b/lib/archethic/contracts/worker.ex index ae5614ca8..95aec0443 100644 --- a/lib/archethic/contracts/worker.ex +++ b/lib/archethic/contracts/worker.ex @@ -86,7 +86,7 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(:transaction, contract, incoming_tx, skip_inherit_check?: true), + Interpreter.execute(:transaction, contract, [incoming_tx], skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do @@ -110,8 +110,9 @@ defmodule Archethic.Contracts.Worker do Logger.debug("Contract execution started", meta) with true <- enough_funds?(contract_tx.address), + {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(trigger_type, contract, nil, skip_inherit_check?: true), + Interpreter.execute(trigger_type, contract, calls, skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do @@ -135,8 +136,9 @@ defmodule Archethic.Contracts.Worker do Logger.debug("Contract execution started", meta) with true <- enough_funds?(contract_tx.address), + {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(trigger_type, contract, nil, skip_inherit_check?: true), + Interpreter.execute(trigger_type, contract, calls, skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do @@ -163,7 +165,7 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(:oracle, contract, oracle_tx, skip_inherit_check?: true), + Interpreter.execute(:oracle, contract, [oracle_tx], skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do diff --git a/lib/archethic/p2p/message/get_contract_calls.ex b/lib/archethic/p2p/message/get_contract_calls.ex new file mode 100644 index 000000000..39ca9ce56 --- /dev/null +++ b/lib/archethic/p2p/message/get_contract_calls.ex @@ -0,0 +1,43 @@ +defmodule Archethic.P2P.Message.GetContractCalls do + @moduledoc """ + Represents a message to request the transactions that called this version of the contract. + """ + @enforce_keys [:address] + defstruct [:address] + + alias Archethic.Contracts + alias Archethic.Crypto + alias Archethic.TransactionChain + alias Archethic.P2P.Message.TransactionList + alias Archethic.Utils + + @type t :: %__MODULE__{ + address: Crypto.versioned_hash() + } + + @spec process(__MODULE__.t(), Crypto.key()) :: TransactionList.t() + def process(%__MODULE__{address: address}, _) do + transaction_addresses = + Contracts.list_contract_transactions(address) + |> Enum.map(&elem(&1, 0)) + + # will crash if a task did not succeed + transactions = + Task.async_stream(transaction_addresses, &TransactionChain.get_transaction(&1)) + |> Stream.map(fn {:ok, {:ok, tx}} -> tx end) + |> Enum.to_list() + + %TransactionList{transactions: transactions, paging_state: nil, more?: false} + end + + @spec serialize(t()) :: bitstring() + def serialize(%__MODULE__{address: address}) do + <> + end + + @spec deserialize(bitstring()) :: {t(), bitstring} + def deserialize(<>) do + {address, rest} = Utils.deserialize_address(rest) + {%__MODULE__{address: address}, rest} + end +end diff --git a/lib/archethic/p2p/message/message_id.ex b/lib/archethic/p2p/message/message_id.ex index 1d941512c..767e40e95 100644 --- a/lib/archethic/p2p/message/message_id.ex +++ b/lib/archethic/p2p/message/message_id.ex @@ -65,7 +65,8 @@ defmodule Archethic.P2P.MessageId do ReplicatePendingTransactionChain, NotifyReplicationValidation, TransactionSummaryMessage, - ReplicationAttestationMessage + ReplicationAttestationMessage, + GetContractCalls } alias Archethic.TransactionChain.{ @@ -118,6 +119,7 @@ defmodule Archethic.P2P.MessageId do ValidateTransaction => 36, ReplicatePendingTransactionChain => 37, NotifyReplicationValidation => 38, + GetContractCalls => 39, # Responses FirstTransactionAddress => 228, diff --git a/lib/archethic/transaction_chain.ex b/lib/archethic/transaction_chain.ex index 15a20c235..2a0144aa2 100644 --- a/lib/archethic/transaction_chain.ex +++ b/lib/archethic/transaction_chain.ex @@ -17,6 +17,7 @@ defmodule Archethic.TransactionChain do AddressList, Error, GenesisAddress, + GetContractCalls, GetGenesisAddress, GetLastTransactionAddress, GetNextAddresses, @@ -635,6 +636,30 @@ defmodule Archethic.TransactionChain do end end + @doc """ + Fetch the transactions (not the inputs) that called the given contract. + """ + @spec fetch_contract_calls(binary()) :: {:ok, list(Transaction.t())} | {:error, term()} + def fetch_contract_calls(contract_address) do + conflict_resolver = fn results -> + results + |> Enum.sort_by(&length(&1.inputs), :desc) + |> List.first() + end + + case P2P.quorum_read( + Election.chain_storage_nodes(contract_address, P2P.authorized_and_available_nodes()), + %GetContractCalls{address: contract_address}, + conflict_resolver + ) do + {:ok, %TransactionList{transactions: transactions}} -> + {:ok, transactions} + + {:error, :network_issue} -> + {:error, :network_issue} + end + end + @doc """ Get a transaction summary from a transaction address """ diff --git a/lib/archethic_web/controllers/api/transaction_controller.ex b/lib/archethic_web/controllers/api/transaction_controller.ex index 1315205bd..340f4fe2d 100644 --- a/lib/archethic_web/controllers/api/transaction_controller.ex +++ b/lib/archethic_web/controllers/api/transaction_controller.ex @@ -219,7 +219,7 @@ defmodule ArchethicWeb.API.TransactionController do defp fetch_recipient_tx_and_simulate(recipient_address, tx) do with {:ok, contract_tx} <- Archethic.search_transaction(recipient_address), {:ok, contract} <- Archethic.parse_contract(contract_tx), - {:ok, _} <- Archethic.execute_contract(:transaction, contract, tx) do + {:ok, _} <- Archethic.execute_contract(:transaction, contract, [tx]) do :ok else # search_transaction errors diff --git a/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs b/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs index 77e351cf0..640f41183 100644 --- a/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs +++ b/test/archethic/contracts/interpreter/legacy/action_interpreter_test.exs @@ -12,12 +12,8 @@ defmodule Archethic.Contracts.Interpreter.Legacy.ActionInterpreterTest do alias Archethic.P2P.Message.FirstTransactionAddress alias Archethic.TransactionChain.Transaction - alias Archethic.TransactionChain.TransactionInput - alias Archethic.TransactionChain.VersionedTransactionInput alias Archethic.TransactionChain.TransactionData - alias Archethic.TransactionFactory - doctest ActionInterpreter import Mox diff --git a/test/archethic/contracts/interpreter/library/contract_test.exs b/test/archethic/contracts/interpreter/library/contract_test.exs index ced826797..377b8129c 100644 --- a/test/archethic/contracts/interpreter/library/contract_test.exs +++ b/test/archethic/contracts/interpreter/library/contract_test.exs @@ -6,6 +6,7 @@ defmodule Archethic.Contracts.Interpreter.Library.ContractTest do use ArchethicCase + alias Archethic.Contracts.ContractConstants, as: Constants alias Archethic.Contracts.Interpreter alias Archethic.Contracts.Interpreter.ActionInterpreter alias Archethic.Contracts.Interpreter.Library.Contract @@ -18,10 +19,6 @@ defmodule Archethic.Contracts.Interpreter.Library.ContractTest do alias Archethic.TransactionChain.TransactionData.Ownership alias Archethic.TransactionChain.TransactionData.UCOLedger alias Archethic.TransactionChain.TransactionData.UCOLedger.Transfer, as: UCOTransfer - alias Archethic.TransactionChain.TransactionInput - alias Archethic.TransactionChain.VersionedTransactionInput - - import Mox doctest Contract @@ -511,7 +508,6 @@ defmodule Archethic.Contracts.Interpreter.Library.ContractTest do describe "get_calls/1" do test "should work" do contract_address = <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>> - call_address = <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>> code = ~s""" actions triggered_by: transaction do @@ -520,28 +516,22 @@ defmodule Archethic.Contracts.Interpreter.Library.ContractTest do end """ - MockDB - |> expect(:get_inputs, fn :call, ^contract_address -> - [ - %VersionedTransactionInput{ - input: %TransactionInput{ - from: call_address, - timestamp: DateTime.utc_now() - }, - protocol_version: ArchethicCase.current_protocol_version() - } - ] - end) - |> expect(:get_transaction, fn ^call_address, _, :io -> - {:ok, %Transaction{data: %TransactionData{}}} - end) - assert %Transaction{ data: %TransactionData{ - content: "1" + content: "2" } } = sanitize_parse_execute(code, %{ + "calls" => [ + Constants.from_transaction(%Transaction{ + data: %TransactionData{}, + address: <<0::16, :crypto.strong_rand_bytes(32)::binary>> + }), + Constants.from_transaction(%Transaction{ + data: %TransactionData{}, + address: <<0::16, :crypto.strong_rand_bytes(32)::binary>> + }) + ], "contract" => %{ "address" => Base.encode16(contract_address) } diff --git a/test/archethic/contracts/interpreter_test.exs b/test/archethic/contracts/interpreter_test.exs index fe07a42ff..a33853c69 100644 --- a/test/archethic/contracts/interpreter_test.exs +++ b/test/archethic/contracts/interpreter_test.exs @@ -139,7 +139,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), - incoming_tx + [incoming_tx] ) end @@ -175,7 +175,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), - incoming_tx + [incoming_tx] ) end @@ -211,7 +211,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), - incoming_tx + [incoming_tx] ) ) end @@ -249,7 +249,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), - incoming_tx + [incoming_tx] ) ) end @@ -288,7 +288,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :oracle, Contract.from_transaction!(contract_tx), - oracle_tx + [oracle_tx] ) ) end @@ -323,7 +323,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), - incoming_tx + [incoming_tx] ) ) end @@ -351,7 +351,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( {:datetime, ~U[2023-03-16 16:28:56Z]}, Contract.from_transaction!(contract_tx), - nil + [] ) end @@ -378,7 +378,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( {:interval, "* * * * *"}, Contract.from_transaction!(contract_tx), - nil + [] ) end @@ -404,7 +404,7 @@ defmodule Archethic.Contracts.InterpreterTest do } assert {:ok, %Transaction{}} = - Interpreter.execute(:oracle, Contract.from_transaction!(contract_tx), nil) + Interpreter.execute(:oracle, Contract.from_transaction!(contract_tx), []) end end end diff --git a/test/archethic/contracts/worker_test.exs b/test/archethic/contracts/worker_test.exs index 1775095ac..b0dd3a76a 100644 --- a/test/archethic/contracts/worker_test.exs +++ b/test/archethic/contracts/worker_test.exs @@ -6,7 +6,7 @@ defmodule Archethic.Contracts.WorkerTest do alias Contracts.{Contract, Interpreter, Worker, ContractConstants} - alias P2P.Message.{Ok, StartMining} + alias P2P.Message.{Ok, StartMining, GetContractCalls, TransactionList} alias TransactionChain.{Transaction, TransactionData, Transaction.ValidationStamp} alias ValidationStamp.{LedgerOperations.UnspentOutput, LedgerOperations.VersionedUnspentOutput} @@ -41,9 +41,13 @@ defmodule Archethic.Contracts.WorkerTest do me = self() MockClient - |> stub(:send_message, fn _, %StartMining{transaction: tx}, _ -> - send(me, {:transaction_sent, tx}) - {:ok, %Ok{}} + |> stub(:send_message, fn + _, %StartMining{transaction: tx}, _ -> + send(me, {:transaction_sent, tx}) + {:ok, %Ok{}} + + _, %GetContractCalls{}, _ -> + {:ok, %TransactionList{transactions: []}} end) aes_key = :crypto.strong_rand_bytes(32) @@ -185,17 +189,10 @@ defmodule Archethic.Contracts.WorkerTest do {:ok, contract} = Interpreter.parse(code) - contract = - %{ - contract - | constants: %ContractConstants{contract: Map.put(constants, "code", code)} - } - |> Map.update!(:triggers, fn triggers -> - Enum.map(triggers, fn {{:interval, interval}, code} -> - {{:interval, interval}, code} - end) - |> Enum.into(%{}) - end) + contract = %{ + contract + | constants: %ContractConstants{contract: Map.put(constants, "code", code)} + } {:ok, _pid} = Worker.start_link(contract) @@ -209,11 +206,11 @@ defmodule Archethic.Contracts.WorkerTest do assert tx.address == expected_tx.address assert tx.data.code == code after - 100_000 -> + 5000 -> raise "Timeout" end after - 100_000 -> + 5000 -> raise "Timeout" end end diff --git a/test/archethic/p2p/message/get_contract_calls_test.exs b/test/archethic/p2p/message/get_contract_calls_test.exs new file mode 100644 index 000000000..df0eef43b --- /dev/null +++ b/test/archethic/p2p/message/get_contract_calls_test.exs @@ -0,0 +1,61 @@ +defmodule Archethic.P2P.Message.GetContractCallsTest do + @moduledoc false + use ArchethicCase + + alias Archethic.Contracts.TransactionLookup + alias Archethic.Crypto + alias Archethic.P2P.Message + alias Archethic.P2P.Message.GetContractCalls + alias Archethic.P2P.Message.TransactionList + alias Archethic.TransactionChain.Transaction + + import Mox + + doctest GetContractCalls + + test "should serialize/deserialize properly" do + msg = %GetContractCalls{address: <<0::16, :crypto.strong_rand_bytes(32)::binary>>} + + assert msg == + msg + |> Message.encode() + |> Message.decode() + |> elem(0) + end + + test "process/2 should work" do + contract_address = <<0::16, :crypto.strong_rand_bytes(32)::binary>> + msg = %GetContractCalls{address: contract_address} + + tx1 = %Transaction{address: <<0::16, :crypto.strong_rand_bytes(32)::binary>>} + tx2 = %Transaction{address: <<0::16, :crypto.strong_rand_bytes(32)::binary>>} + tx1_address = tx1.address + tx2_address = tx2.address + + TransactionLookup.add_contract_transaction( + contract_address, + tx1.address, + DateTime.utc_now(), + ArchethicCase.current_protocol_version() + ) + + TransactionLookup.add_contract_transaction( + contract_address, + tx2.address, + DateTime.utc_now(), + ArchethicCase.current_protocol_version() + ) + + MockDB + |> expect(:get_transaction, fn ^tx1_address, _, _ -> + {:ok, tx1} + end) + |> expect(:get_transaction, fn ^tx2_address, _, _ -> + {:ok, tx2} + end) + + # asserts + assert %TransactionList{transactions: [^tx1, ^tx2]} = + GetContractCalls.process(msg, Crypto.first_node_public_key()) + end +end From ccd997f19c9b7b1bc1ba79b5ea801e35c3153631 Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Thu, 6 Apr 2023 18:58:04 +0200 Subject: [PATCH 3/5] fetch the calls on trigger: transaction|oracle as well --- lib/archethic.ex | 3 +- lib/archethic/contracts/interpreter.ex | 43 +++++------ lib/archethic/contracts/worker.ex | 16 +++-- .../controllers/api/transaction_controller.ex | 8 +-- test/archethic/contracts/interpreter_test.exs | 72 ++++++++++++++++++- 5 files changed, 110 insertions(+), 32 deletions(-) diff --git a/lib/archethic.ex b/lib/archethic.ex index 19db047d1..018902f10 100644 --- a/lib/archethic.ex +++ b/lib/archethic.ex @@ -323,6 +323,7 @@ defmodule Archethic do @spec execute_contract( Contract.trigger_type(), Contract.t(), + nil | Transaction.t(), [Transaction.t()] ) :: {:ok, nil | Transaction.t()} @@ -332,7 +333,7 @@ defmodule Archethic do | :invalid_transaction_constraints | :invalid_oracle_constraints | :invalid_inherit_constraints} - defdelegate execute_contract(trigger_type, contract, calls), + defdelegate execute_contract(trigger_type, contract, maybe_trigger_tx, calls), to: Interpreter, as: :execute diff --git a/lib/archethic/contracts/interpreter.ex b/lib/archethic/contracts/interpreter.ex index 307f55830..6e6263676 100644 --- a/lib/archethic/contracts/interpreter.ex +++ b/lib/archethic/contracts/interpreter.ex @@ -116,6 +116,7 @@ defmodule Archethic.Contracts.Interpreter do @spec execute( Contract.trigger_type(), Contract.t(), + nil | Transaction.t(), [Transaction.t()], execute_opts() ) :: @@ -129,6 +130,7 @@ defmodule Archethic.Contracts.Interpreter do def execute( trigger_type, contract = %Contract{triggers: triggers}, + maybe_trigger_tx, calls, opts \\ [] ) do @@ -141,8 +143,8 @@ defmodule Archethic.Contracts.Interpreter do trigger_type, trigger_code, contract, + maybe_trigger_tx, calls, - contract, opts ) end @@ -216,24 +218,24 @@ defmodule Archethic.Contracts.Interpreter do defp do_execute( :transaction, trigger_code, - contract, - calls = [incoming_tx = %Transaction{}], - %Contract{ + contract = %Contract{ version: version, conditions: conditions, constants: %Constants{ contract: contract_constants } }, + trigger_tx = %Transaction{}, + calls, opts ) do constants = %{ - "transaction" => Constants.from_transaction(incoming_tx), + "transaction" => Constants.from_transaction(trigger_tx), "contract" => contract_constants } if valid_conditions?(version, conditions.transaction, constants) do - case execute_trigger(version, trigger_code, contract, calls) do + case execute_trigger(version, trigger_code, contract, trigger_tx, calls) do nil -> {:ok, nil} @@ -256,24 +258,24 @@ defmodule Archethic.Contracts.Interpreter do defp do_execute( :oracle, trigger_code, - contract, - calls = [oracle_tx = %Transaction{}], - %Contract{ + contract = %Contract{ version: version, conditions: conditions, constants: %Constants{ contract: contract_constants } }, + trigger_tx = %Transaction{}, + calls, opts ) do constants = %{ - "transaction" => Constants.from_transaction(oracle_tx), + "transaction" => Constants.from_transaction(trigger_tx), "contract" => contract_constants } if valid_conditions?(version, conditions.oracle, constants) do - case execute_trigger(version, trigger_code, contract, calls) do + case execute_trigger(version, trigger_code, contract, trigger_tx, calls) do nil -> {:ok, nil} @@ -292,12 +294,12 @@ defmodule Archethic.Contracts.Interpreter do defp do_execute( _trigger_type, trigger_code, - contract, + contract = %Contract{version: version}, + nil, calls, - %Contract{version: version}, opts ) do - case execute_trigger(version, trigger_code, contract, calls) do + case execute_trigger(version, trigger_code, contract, nil, calls) do nil -> {:ok, nil} @@ -314,18 +316,19 @@ defmodule Archethic.Contracts.Interpreter do version, trigger_code, contract, + maybe_trigger_tx, calls ) do constants_trigger = %{ "calls" => Enum.map(calls, &Constants.from_transaction/1), "transaction" => - case calls do - [tx] -> - # :oracle & :transaction - Constants.from_transaction(tx) - - _ -> + case maybe_trigger_tx do + nil -> nil + + trigger_tx -> + # :oracle & :transaction + Constants.from_transaction(trigger_tx) end, "contract" => contract.constants.contract } diff --git a/lib/archethic/contracts/worker.ex b/lib/archethic/contracts/worker.ex index 95aec0443..604fe7b53 100644 --- a/lib/archethic/contracts/worker.ex +++ b/lib/archethic/contracts/worker.ex @@ -76,17 +76,20 @@ defmodule Archethic.Contracts.Worker do # TRIGGER: TRANSACTION def handle_cast( - {:execute, incoming_tx = %Transaction{}}, + {:execute, trigger_tx = %Transaction{}}, state = %{contract: contract} ) do contract_tx = Constants.to_transaction(contract.constants.contract) - meta = log_metadata(contract_tx, incoming_tx) + meta = log_metadata(contract_tx, trigger_tx) Logger.debug("Contract execution started", meta) with true <- enough_funds?(contract_tx.address), + {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(:transaction, contract, [incoming_tx], skip_inherit_check?: true), + Interpreter.execute(:transaction, contract, trigger_tx, calls, + skip_inherit_check?: true + ), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do @@ -112,7 +115,7 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(trigger_type, contract, calls, skip_inherit_check?: true), + Interpreter.execute(trigger_type, contract, nil, calls, skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do @@ -138,7 +141,7 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(trigger_type, contract, calls, skip_inherit_check?: true), + Interpreter.execute(trigger_type, contract, nil, calls, skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do @@ -164,8 +167,9 @@ defmodule Archethic.Contracts.Worker do Logger.debug("Contract execution started", meta) with true <- enough_funds?(contract_tx.address), + {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(:oracle, contract, [oracle_tx], skip_inherit_check?: true), + Interpreter.execute(:oracle, contract, oracle_tx, calls, skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do diff --git a/lib/archethic_web/controllers/api/transaction_controller.ex b/lib/archethic_web/controllers/api/transaction_controller.ex index 340f4fe2d..b98b745ea 100644 --- a/lib/archethic_web/controllers/api/transaction_controller.ex +++ b/lib/archethic_web/controllers/api/transaction_controller.ex @@ -154,7 +154,7 @@ defmodule ArchethicWeb.API.TransactionController do ) do case TransactionPayload.changeset(params) do changeset = %{valid?: true} -> - tx = + trigger_tx = %Transaction{data: %TransactionData{recipients: recipients}} = changeset |> TransactionPayload.to_map() @@ -164,7 +164,7 @@ defmodule ArchethicWeb.API.TransactionController do Task.Supervisor.async_stream_nolink( Archethic.TaskSupervisor, recipients, - &fetch_recipient_tx_and_simulate(&1, tx), + &fetch_recipient_tx_and_simulate(&1, trigger_tx), on_timeout: :kill_task, timeout: 5000 ) @@ -216,10 +216,10 @@ defmodule ArchethicWeb.API.TransactionController do end end - defp fetch_recipient_tx_and_simulate(recipient_address, tx) do + defp fetch_recipient_tx_and_simulate(recipient_address, trigger_tx) do with {:ok, contract_tx} <- Archethic.search_transaction(recipient_address), {:ok, contract} <- Archethic.parse_contract(contract_tx), - {:ok, _} <- Archethic.execute_contract(:transaction, contract, [tx]) do + {:ok, _} <- Archethic.execute_contract(:transaction, contract, trigger_tx, [trigger_tx]) do :ok else # search_transaction errors diff --git a/test/archethic/contracts/interpreter_test.exs b/test/archethic/contracts/interpreter_test.exs index a33853c69..3c138c6fb 100644 --- a/test/archethic/contracts/interpreter_test.exs +++ b/test/archethic/contracts/interpreter_test.exs @@ -139,6 +139,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), + incoming_tx, [incoming_tx] ) end @@ -175,6 +176,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), + incoming_tx, [incoming_tx] ) end @@ -211,6 +213,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), + incoming_tx, [incoming_tx] ) ) @@ -249,6 +252,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), + incoming_tx, [incoming_tx] ) ) @@ -288,6 +292,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :oracle, Contract.from_transaction!(contract_tx), + oracle_tx, [oracle_tx] ) ) @@ -323,6 +328,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( :transaction, Contract.from_transaction!(contract_tx), + incoming_tx, [incoming_tx] ) ) @@ -351,6 +357,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( {:datetime, ~U[2023-03-16 16:28:56Z]}, Contract.from_transaction!(contract_tx), + nil, [] ) end @@ -378,6 +385,7 @@ defmodule Archethic.Contracts.InterpreterTest do Interpreter.execute( {:interval, "* * * * *"}, Contract.from_transaction!(contract_tx), + nil, [] ) end @@ -403,8 +411,70 @@ defmodule Archethic.Contracts.InterpreterTest do } } + oracle_tx = %Transaction{ + type: :oracle, + data: %TransactionData{} + } + assert {:ok, %Transaction{}} = - Interpreter.execute(:oracle, Contract.from_transaction!(contract_tx), []) + Interpreter.execute( + :oracle, + Contract.from_transaction!(contract_tx), + oracle_tx, + [oracle_tx] + ) + end + + test "should be able to get_calls" do + code = ~s""" + @version 1 + condition inherit: [ + content: true + ] + + condition transaction: [] + + actions triggered_by: transaction do + calls = Contract.get_calls() + Contract.set_content List.size(calls) + end + """ + + contract_tx = %Transaction{ + type: :contract, + data: %TransactionData{ + code: code + } + } + + incoming_tx = %Transaction{ + type: :transfer, + data: %TransactionData{} + } + + calls = [ + %Transaction{ + type: :transfer, + data: %TransactionData{} + }, + %Transaction{ + type: :transfer, + data: %TransactionData{} + }, + %Transaction{ + type: :transfer, + data: %TransactionData{} + }, + incoming_tx + ] + + assert {:ok, %Transaction{data: %TransactionData{content: "4"}}} = + Interpreter.execute( + :transaction, + Contract.from_transaction!(contract_tx), + incoming_tx, + calls + ) end end end From 79595eb12295add228ec1e7e84a8a696846a091f Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Thu, 20 Apr 2023 12:08:03 +0200 Subject: [PATCH 4/5] append the trigger_tx to the calls in case it is missing due to race condition --- lib/archethic/contracts/worker.ex | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/archethic/contracts/worker.ex b/lib/archethic/contracts/worker.ex index 604fe7b53..0d10fc616 100644 --- a/lib/archethic/contracts/worker.ex +++ b/lib/archethic/contracts/worker.ex @@ -87,7 +87,12 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(:transaction, contract, trigger_tx, calls, + Interpreter.execute( + :transaction, + contract, + trigger_tx, + # we append the trigger_tx to the calls in case it is missing due to race condition + Enum.uniq([trigger_tx | calls]), skip_inherit_check?: true ), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), @@ -169,7 +174,14 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute(:oracle, contract, oracle_tx, calls, skip_inherit_check?: true), + Interpreter.execute( + :oracle, + contract, + oracle_tx, + # we append the oracle_tx to the calls in case it is missing due to race condition + Enum.uniq([oracle_tx | calls]), + skip_inherit_check?: true + ), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do From 17bf65d6a0df41f2d4a8e56efe0cfbd7a7816fe9 Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Thu, 20 Apr 2023 15:55:44 +0200 Subject: [PATCH 5/5] oracle tx should not be part of the calls --- lib/archethic/contracts/worker.ex | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/archethic/contracts/worker.ex b/lib/archethic/contracts/worker.ex index 0d10fc616..81465e674 100644 --- a/lib/archethic/contracts/worker.ex +++ b/lib/archethic/contracts/worker.ex @@ -174,14 +174,7 @@ defmodule Archethic.Contracts.Worker do with true <- enough_funds?(contract_tx.address), {:ok, calls} <- TransactionChain.fetch_contract_calls(contract_tx.address), {:ok, next_tx = %Transaction{}} <- - Interpreter.execute( - :oracle, - contract, - oracle_tx, - # we append the oracle_tx to the calls in case it is missing due to race condition - Enum.uniq([oracle_tx | calls]), - skip_inherit_check?: true - ), + Interpreter.execute(:oracle, contract, oracle_tx, calls, skip_inherit_check?: true), {:ok, next_tx} <- chain_transaction(next_tx, contract_tx), :ok <- ensure_enough_funds(next_tx, contract_tx.address), :ok <- handle_new_transaction(next_tx) do