From 3a673b0bebd9f20bfec465ab67913784aa10695c Mon Sep 17 00:00:00 2001 From: apoorv-2204 Date: Fri, 27 Jan 2023 12:40:29 +0530 Subject: [PATCH] worker_should_not_be_executed_while_replicating_a_transaction_from_self_repair_ --- lib/archethic/contracts.ex | 4 +- lib/archethic/contracts/loader.ex | 93 ++++++++++++++++++------------- lib/archethic/replication.ex | 17 +++--- test/archethic/bootstrap_test.exs | 2 +- 4 files changed, 65 insertions(+), 51 deletions(-) diff --git a/lib/archethic/contracts.ex b/lib/archethic/contracts.ex index b8e55539c..e0abe15e9 100644 --- a/lib/archethic/contracts.ex +++ b/lib/archethic/contracts.ex @@ -133,8 +133,8 @@ defmodule Archethic.Contracts do @doc """ Load transaction into the Smart Contract context leveraging the interpreter """ - @spec load_transaction(Transaction.t()) :: :ok - defdelegate load_transaction(tx), to: Loader + @spec load_transaction(Transaction.t(), list()) :: :ok + defdelegate load_transaction(tx, opts \\ []), to: Loader @spec accept_new_contract?(Transaction.t() | nil, Transaction.t(), DateTime.t()) :: boolean() def accept_new_contract?(nil, _, _), do: true diff --git a/lib/archethic/contracts/loader.ex b/lib/archethic/contracts/loader.ex index b0f269525..f1b68a45b 100644 --- a/lib/archethic/contracts/loader.ex +++ b/lib/archethic/contracts/loader.ex @@ -34,7 +34,7 @@ defmodule Archethic.Contracts.Loader do _ -> true end) |> Stream.map(fn {:ok, tx} -> tx end) - |> Stream.each(&load_transaction(&1, true)) + |> Stream.each(&load_transaction(&1, from_db: true)) |> Stream.run() {:ok, []} @@ -43,18 +43,24 @@ defmodule Archethic.Contracts.Loader do @doc """ Load the smart contracts based on transaction involving smart contract code """ - @spec load_transaction(Transaction.t()) :: :ok - def load_transaction(_tx, from_db \\ false) - - def load_transaction( - tx = %Transaction{ - address: address, - type: type, - data: %TransactionData{code: code} - }, - _from_db - ) - when code != "" do + @spec load_transaction(Transaction.t(), list()) :: :ok + def load_transaction(tx, opts \\ []) do + from_db = Keyword.get(opts, :from_db, false) + from_self_repair = Keyword.get(opts, :from_self_repair, false) + + do_load_transaction(tx, from_db, from_self_repair) + end + + defp do_load_transaction( + tx = %Transaction{ + address: address, + type: type, + data: %TransactionData{code: code} + }, + _from_db, + _from_self_repair + ) + when code != "" do stop_contract(Transaction.previous_address(tx)) %Contract{triggers: triggers} = Contracts.parse!(code) @@ -77,25 +83,31 @@ defmodule Archethic.Contracts.Loader do end end - def load_transaction( - tx = %Transaction{ - address: tx_address, - type: tx_type, - validation_stamp: %ValidationStamp{ - timestamp: tx_timestamp, - recipients: recipients, - protocol_version: protocol_version - } - }, - false - ) - when recipients != [] do + defp do_load_transaction( + tx = %Transaction{ + address: tx_address, + type: tx_type, + validation_stamp: %ValidationStamp{ + timestamp: tx_timestamp, + recipients: recipients, + protocol_version: protocol_version + } + }, + _from_db = false, + from_self_repair? + ) + when recipients != [] do Enum.each(recipients, fn contract_address -> Logger.info("Execute transaction on contract #{Base.encode16(contract_address)}", transaction_address: Base.encode16(tx_address), transaction_type: tx_type ) + unless from_self_repair? do + # execute asynchronously the contract + Worker.execute(contract_address, tx) + end + TransactionLookup.add_contract_transaction( contract_address, tx_address, @@ -112,19 +124,20 @@ defmodule Archethic.Contracts.Loader do end) end - def load_transaction( - %Transaction{ - address: address, - type: type, - validation_stamp: %ValidationStamp{ - recipients: recipients, - timestamp: timestamp, - protocol_version: protocol_version - } - }, - true - ) - when recipients != [] do + defp do_load_transaction( + %Transaction{ + address: address, + type: type, + validation_stamp: %ValidationStamp{ + recipients: recipients, + timestamp: timestamp, + protocol_version: protocol_version + } + }, + _from_db = true, + false + ) + when recipients != [] do Enum.each( recipients, &TransactionLookup.add_contract_transaction(&1, address, timestamp, protocol_version) @@ -136,7 +149,7 @@ defmodule Archethic.Contracts.Loader do ) end - def load_transaction(_tx, _), do: :ok + defp do_load_transaction(_tx, _, _), do: :ok @doc """ Termine a contract execution diff --git a/lib/archethic/replication.ex b/lib/archethic/replication.ex index 892bbdd40..55526c338 100644 --- a/lib/archethic/replication.ex +++ b/lib/archethic/replication.ex @@ -136,7 +136,8 @@ defmodule Archethic.Replication do type: type, validation_stamp: %ValidationStamp{timestamp: timestamp} }, - download_nodes \\ P2P.authorized_and_available_nodes() + download_nodes \\ P2P.authorized_and_available_nodes(), + self_repair? \\ false ) do start_time = System.monotonic_time() @@ -155,13 +156,13 @@ defmodule Archethic.Replication do |> Election.chain_storage_nodes(download_nodes) |> Utils.key_in_node_list?(Crypto.first_node_public_key()) - if storage_node?, do: ingest_transaction(tx, false) + if storage_node?, do: ingest_transaction(tx, false, self_repair?) end) |> Stream.run() TransactionChain.write_transaction(tx) - :ok = ingest_transaction(tx, false) + :ok = ingest_transaction(tx, false, self_repair?) Logger.info("Replication finished", transaction_address: Base.encode16(address), @@ -202,7 +203,7 @@ defmodule Archethic.Replication do ) do case validate_transaction(tx, self_repair?, download_nodes) do :ok -> - sync_transaction_chain(tx, download_nodes) + sync_transaction_chain(tx, download_nodes, self_repair?) {:error, reason} -> Logger.warning("Invalid transaction for replication - #{inspect(reason)}", @@ -247,7 +248,7 @@ defmodule Archethic.Replication do case TransactionValidator.validate(tx, self_repair?) do :ok -> :ok = TransactionChain.write_transaction(tx, :io) - ingest_transaction(tx, true) + ingest_transaction(tx, true, self_repair?) Logger.info("Replication finished", transaction_address: Base.encode16(address), @@ -564,14 +565,14 @@ defmodule Archethic.Replication do - Transactions with smart contract deploy instances of them or can put in pending state waiting approval signatures - Code approval transactions may trigger the TestNets deployments or hot-reloads """ - @spec ingest_transaction(Transaction.t(), boolean()) :: :ok - def ingest_transaction(tx = %Transaction{}, io_transaction?) do + @spec ingest_transaction(Transaction.t(), boolean(), boolean()) :: :ok + def ingest_transaction(tx = %Transaction{}, io_transaction?, self_repair?) do TransactionChain.load_transaction(tx) Crypto.load_transaction(tx) P2P.load_transaction(tx) SharedSecrets.load_transaction(tx) Account.load_transaction(tx, io_transaction?) - Contracts.load_transaction(tx) + Contracts.load_transaction(tx, from_self_repair: self_repair?) OracleChain.load_transaction(tx) Reward.load_transaction(tx) :ok diff --git a/test/archethic/bootstrap_test.exs b/test/archethic/bootstrap_test.exs index 27aaa22ea..bd1ce4a79 100644 --- a/test/archethic/bootstrap_test.exs +++ b/test/archethic/bootstrap_test.exs @@ -264,7 +264,7 @@ defmodule Archethic.BootstrapTest do validated_tx = %{tx | validation_stamp: stamp} :ok = TransactionChain.write_transaction(validated_tx) - :ok = Replication.ingest_transaction(validated_tx, false) + :ok = Replication.ingest_transaction(validated_tx, false, false) {:ok, %Ok{}}