From 01f836043431ae6f98e13dc5a8e048302713070e Mon Sep 17 00:00:00 2001 From: Neylix Date: Thu, 23 Feb 2023 15:48:13 +0100 Subject: [PATCH 1/3] Load all transaction when bootsrapping SC Loader --- lib/archethic/contracts.ex | 2 +- lib/archethic/contracts/loader.ex | 33 ++++++++++++------------------- lib/archethic/replication.ex | 2 +- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/lib/archethic/contracts.ex b/lib/archethic/contracts.ex index e0abe15e9..04b0bd65e 100644 --- a/lib/archethic/contracts.ex +++ b/lib/archethic/contracts.ex @@ -134,7 +134,7 @@ defmodule Archethic.Contracts do Load transaction into the Smart Contract context leveraging the interpreter """ @spec load_transaction(Transaction.t(), list()) :: :ok - defdelegate load_transaction(tx, opts \\ []), to: Loader + 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 2f3dd5ce7..765f53e90 100644 --- a/lib/archethic/contracts/loader.ex +++ b/lib/archethic/contracts/loader.ex @@ -9,8 +9,6 @@ defmodule Archethic.Contracts.Loader do alias Archethic.Contracts.TransactionLookup alias Archethic.Contracts.Worker - alias Archethic.DB - alias Archethic.TransactionChain alias Archethic.TransactionChain.Transaction alias Archethic.TransactionChain.Transaction.ValidationStamp @@ -26,15 +24,16 @@ defmodule Archethic.Contracts.Loader do end def init(_opts) do - DB.list_last_transaction_addresses() - |> Stream.map(&DB.get_transaction(&1, [])) - |> Stream.filter(fn - {:ok, %Transaction{data: %TransactionData{code: ""}}} -> false - {:error, _} -> false - _ -> true - end) - |> Stream.map(fn {:ok, tx} -> tx end) - |> Stream.each(&load_transaction(&1, from_db: true)) + TransactionChain.list_io_transactions([]) + |> Stream.filter(&(&1.data.recipients != [])) + |> Stream.each(&load_transaction(&1, execute_contract?: false)) + |> Stream.run() + + # Network transactions does not contains trigger or recipient + TransactionChain.list_all([]) + |> Stream.reject(&Transaction.network_type?(&1.type)) + |> Stream.filter(&(&1.data.recipients != [] or &1.data.code != "")) + |> Stream.each(&load_transaction(&1, execute_contract?: false)) |> Stream.run() {:ok, []} @@ -55,16 +54,10 @@ defmodule Archethic.Contracts.Loader do protocol_version: protocol_version } }, - opts \\ [] + execute_contract?: execute_contract? ) do - from_db? = Keyword.get(opts, :from_db, false) - from_self_repair? = Keyword.get(opts, :from_self_repair, false) - if from_db? and from_self_repair?, do: raise("Cant have tx with db and self repair flag") - # Stop previous transaction contract - unless from_db? do - stop_contract(Transaction.previous_address(tx)) - end + stop_contract(Transaction.previous_address(tx)) # If transaction contains code, start a new worker for it if code != "" do @@ -95,7 +88,7 @@ defmodule Archethic.Contracts.Loader do protocol_version ) - unless from_db? or from_self_repair? do + if execute_contract? do # execute contract asynchronously only if we are in live replication Logger.info( "Execute transaction on contract #{Base.encode16(contract_address)}", diff --git a/lib/archethic/replication.ex b/lib/archethic/replication.ex index 55526c338..18fb6a083 100644 --- a/lib/archethic/replication.ex +++ b/lib/archethic/replication.ex @@ -572,7 +572,7 @@ defmodule Archethic.Replication do P2P.load_transaction(tx) SharedSecrets.load_transaction(tx) Account.load_transaction(tx, io_transaction?) - Contracts.load_transaction(tx, from_self_repair: self_repair?) + Contracts.load_transaction(tx, execute_contract?: not self_repair?) OracleChain.load_transaction(tx) Reward.load_transaction(tx) :ok From b80fca5b78a8be435cfc30508aa8309b28e8896e Mon Sep 17 00:00:00 2001 From: Neylix Date: Thu, 23 Feb 2023 16:08:36 +0100 Subject: [PATCH 2/3] Start SC Worker only on storage node --- lib/archethic/contracts/loader.ex | 11 ++++++----- lib/archethic/replication.ex | 7 ++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/archethic/contracts/loader.ex b/lib/archethic/contracts/loader.ex index 765f53e90..3a3e96558 100644 --- a/lib/archethic/contracts/loader.ex +++ b/lib/archethic/contracts/loader.ex @@ -26,14 +26,14 @@ defmodule Archethic.Contracts.Loader do def init(_opts) do TransactionChain.list_io_transactions([]) |> Stream.filter(&(&1.data.recipients != [])) - |> Stream.each(&load_transaction(&1, execute_contract?: false)) + |> Stream.each(&load_transaction(&1, execute_contract?: false, io_transaction?: true)) |> Stream.run() # Network transactions does not contains trigger or recipient TransactionChain.list_all([]) |> Stream.reject(&Transaction.network_type?(&1.type)) |> Stream.filter(&(&1.data.recipients != [] or &1.data.code != "")) - |> Stream.each(&load_transaction(&1, execute_contract?: false)) + |> Stream.each(&load_transaction(&1, execute_contract?: false, io_transaction?: false)) |> Stream.run() {:ok, []} @@ -54,13 +54,14 @@ defmodule Archethic.Contracts.Loader do protocol_version: protocol_version } }, - execute_contract?: execute_contract? + execute_contract?: execute_contract?, + io_transaction?: io_transaction? ) do # Stop previous transaction contract stop_contract(Transaction.previous_address(tx)) - # If transaction contains code, start a new worker for it - if code != "" do + # If transaction contains code and we are storage node, start a new worker for it + if code != "" and not io_transaction? do %Contract{triggers: triggers} = Contracts.parse!(code) triggers = Enum.reject(triggers, fn {_, actions} -> actions == {:__block__, [], []} end) diff --git a/lib/archethic/replication.ex b/lib/archethic/replication.ex index 18fb6a083..dceb0aa8f 100644 --- a/lib/archethic/replication.ex +++ b/lib/archethic/replication.ex @@ -572,7 +572,12 @@ defmodule Archethic.Replication do P2P.load_transaction(tx) SharedSecrets.load_transaction(tx) Account.load_transaction(tx, io_transaction?) - Contracts.load_transaction(tx, execute_contract?: not self_repair?) + + Contracts.load_transaction(tx, + execute_contract?: not self_repair?, + io_transaction?: io_transaction? + ) + OracleChain.load_transaction(tx) Reward.load_transaction(tx) :ok From eef82b71aff6522c5b031e72e51512a474522690 Mon Sep 17 00:00:00 2001 From: Neylix Date: Mon, 27 Feb 2023 16:59:08 +0100 Subject: [PATCH 3/3] Update test --- test/archethic/contracts/loader_test.exs | 61 +++++++++++------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/test/archethic/contracts/loader_test.exs b/test/archethic/contracts/loader_test.exs index 5d3683ac1..792ae3a0e 100644 --- a/test/archethic/contracts/loader_test.exs +++ b/test/archethic/contracts/loader_test.exs @@ -38,7 +38,7 @@ defmodule Archethic.Contracts.LoaderTest do } } - assert :ok = Loader.load_transaction(tx) + assert :ok = Loader.load_transaction(tx, execute_contract?: false, io_transaction?: false) [{pid, _}] = Registry.lookup(ContractRegistry, contract_address) assert Enum.any?( @@ -107,9 +107,9 @@ defmodule Archethic.Contracts.LoaderTest do } } - assert :ok = Loader.load_transaction(tx1) + assert :ok = Loader.load_transaction(tx1, execute_contract?: false, io_transaction?: false) [{pid1, _}] = Registry.lookup(ContractRegistry, tx1.address) - assert :ok = Loader.load_transaction(tx2) + assert :ok = Loader.load_transaction(tx2, execute_contract?: false, io_transaction?: false) [{pid2, _}] = Registry.lookup(ContractRegistry, tx2.address) assert !Process.alive?(pid1) @@ -128,36 +128,33 @@ defmodule Archethic.Contracts.LoaderTest do contract_address = Crypto.derive_address(pub1) + tx = %Transaction{ + address: contract_address, + data: %TransactionData{ + code: """ + condition transaction: [ + content: "hello" + ] + + condition inherit: [ + content: "hi" + ] + + actions triggered_by: transaction do + set_content "hi" + end + """ + }, + previous_public_key: pub0, + validation_stamp: %ValidationStamp{ + recipients: [], + timestamp: DateTime.utc_now() + } + } + MockDB - |> stub(:list_last_transaction_addresses, fn -> - [contract_address] - end) - |> stub(:get_transaction, fn ^contract_address, _ -> - {:ok, - %Transaction{ - address: contract_address, - data: %TransactionData{ - code: """ - condition transaction: [ - content: "hello" - ] - - condition inherit: [ - content: "hi" - ] - - actions triggered_by: transaction do - set_content "hi" - end - """ - }, - previous_public_key: pub0, - validation_stamp: %ValidationStamp{ - recipients: [], - timestamp: DateTime.utc_now() - } - }} - end) + |> stub(:list_io_transactions, fn _ -> [] end) + |> stub(:list_transactions, fn _ -> [tx] end) assert {:ok, _} = Loader.start_link() [{pid, _}] = Registry.lookup(ContractRegistry, contract_address)