Skip to content

Commit

Permalink
Remove unused flag and node list in replication
Browse files Browse the repository at this point in the history
  • Loading branch information
Neylix authored and samuelmanzanera committed Mar 24, 2023
1 parent 9d4a8bd commit e974dde
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 106 deletions.
2 changes: 1 addition & 1 deletion lib/archethic/bootstrap/network_init.ex
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ defmodule Archethic.Bootstrap.NetworkInit do

@spec self_replication(Transaction.t()) :: :ok
def self_replication(tx = %Transaction{}) do
:ok = Replication.validate_and_store_transaction_chain(tx)
:ok = Replication.sync_transaction_chain(tx)

tx_summary = TransactionSummary.from_transaction(tx)

Expand Down
66 changes: 18 additions & 48 deletions lib/archethic/replication.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,9 @@ defmodule Archethic.Replication do
It will download the transaction chain and unspents to validate the new transaction and store the new transaction in the pool awaiting commitment
"""
@spec validate_transaction(
tx :: Transaction.t(),
self_repair? :: boolean(),
download_nodes :: list(Node.t())
) ::
@spec validate_transaction(tx :: Transaction.t()) ::
:ok | {:error, TransactionValidator.error()} | {:error, :transaction_already_exists}
def validate_transaction(
tx = %Transaction{address: address, type: type},
self_repair? \\ false,
download_nodes \\ P2P.authorized_and_available_nodes()
) do
def validate_transaction(tx = %Transaction{address: address, type: type}) do
if TransactionChain.transaction_exists?(address) do
Logger.warning("Transaction already exists",
transaction_address: Base.encode16(address),
Expand All @@ -78,14 +70,9 @@ defmodule Archethic.Replication do
transaction_type: type
)

{previous_tx, inputs} = fetch_context(tx, self_repair?, download_nodes)
{previous_tx, inputs} = fetch_context(tx)
# Validate the transaction and check integrity from the previous transaction
case TransactionValidator.validate(
tx,
previous_tx,
inputs,
self_repair?
) do
case TransactionValidator.validate(tx, previous_tx, inputs) do
:ok ->
Logger.info("Replication validation finished",
transaction_address: Base.encode16(address),
Expand Down Expand Up @@ -189,23 +176,12 @@ defmodule Archethic.Replication do
It will download the transaction chain and unspents to validate the new transaction and store the new transaction chain
and update the internal ledger and views
"""
@spec validate_and_store_transaction_chain(
validated_tx :: Transaction.t(),
self_repair? :: boolean(),
download_nodes :: list(Node.t())
) ::
@spec validate_and_store_transaction_chain(validated_tx :: Transaction.t()) ::
:ok | {:error, TransactionValidator.error()} | {:error, :transaction_already_exists}
def validate_and_store_transaction_chain(
tx = %Transaction{
address: address,
type: type
},
self_repair? \\ false,
download_nodes \\ P2P.authorized_and_available_nodes()
) do
case validate_transaction(tx, self_repair?, download_nodes) do
def validate_and_store_transaction_chain(tx = %Transaction{address: address, type: type}) do
case validate_transaction(tx) do
:ok ->
sync_transaction_chain(tx, download_nodes, self_repair?)
sync_transaction_chain(tx)

{:error, reason} ->
Logger.warning("Invalid transaction for replication - #{inspect(reason)}",
Expand All @@ -222,12 +198,9 @@ defmodule Archethic.Replication do
It will validate the new transaction and store the new transaction updating then the internals ledgers and views
"""
@spec validate_and_store_transaction(Transaction.t(), boolean()) ::
@spec validate_and_store_transaction(Transaction.t()) ::
:ok | {:error, TransactionValidator.error()} | {:error, :transaction_already_exists}
def validate_and_store_transaction(
tx = %Transaction{address: address, type: type},
self_repair? \\ false
) do
def validate_and_store_transaction(tx = %Transaction{address: address, type: type}) do
if TransactionChain.transaction_exists?(address, :io) do
Logger.warning("Transaction already exists",
transaction_address: Base.encode16(address),
Expand All @@ -243,9 +216,9 @@ defmodule Archethic.Replication do
transaction_type: type
)

case TransactionValidator.validate(tx, self_repair?) do
case TransactionValidator.validate(tx) do
:ok ->
synchronize_io_transaction(tx, self_repair?)
synchronize_io_transaction(tx)

:telemetry.execute(
[:archethic, :replication, :validation],
Expand Down Expand Up @@ -280,7 +253,7 @@ defmodule Archethic.Replication do
type: type,
validation_stamp: %ValidationStamp{timestamp: timestamp}
},
self_repair?
self_repair? \\ false
) do
:ok = TransactionChain.write_transaction(tx, :io)
ingest_transaction(tx, true, self_repair?)
Expand All @@ -293,7 +266,7 @@ defmodule Archethic.Replication do
PubSub.notify_new_transaction(address, type, timestamp)
end

defp fetch_context(tx = %Transaction{}, self_repair?, download_nodes) do
defp fetch_context(tx = %Transaction{}) do
previous_address = Transaction.previous_address(tx)

Logger.debug(
Expand All @@ -317,11 +290,11 @@ defmodule Archethic.Replication do
transaction_type: tx.type
)

TransactionContext.fetch_transaction(previous_address, download_nodes)
TransactionContext.fetch_transaction(previous_address)
end
end)

inputs = if self_repair?, do: [], else: fetch_inputs(tx, download_nodes)
inputs = fetch_inputs(tx)
previous_transaction = Task.await(previous_transaction_task, Message.get_max_timeout() + 1000)

Logger.debug("Previous transaction #{inspect(previous_transaction)}",
Expand All @@ -332,10 +305,7 @@ defmodule Archethic.Replication do
{previous_transaction, inputs}
end

defp fetch_inputs(
tx = %Transaction{validation_stamp: %ValidationStamp{timestamp: tx_time}},
download_nodes
) do
defp fetch_inputs(tx = %Transaction{validation_stamp: %ValidationStamp{timestamp: tx_time}}) do
previous_address = Transaction.previous_address(tx)

Logger.debug(
Expand All @@ -345,7 +315,7 @@ defmodule Archethic.Replication do
)

previous_address
|> TransactionContext.fetch_transaction_inputs(tx_time, download_nodes)
|> TransactionContext.fetch_transaction_inputs(tx_time)
|> tap(fn inputs ->
Logger.debug("Got #{inspect(inputs)} for #{Base.encode16(previous_address)}",
transaction_address: Base.encode16(tx.address),
Expand Down
14 changes: 8 additions & 6 deletions lib/archethic/replication/transaction_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ defmodule Archethic.Replication.TransactionContext do
alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.TransactionInput

alias Archethic.P2P

require Logger

@doc """
Fetch transaction
"""
@spec fetch_transaction(address :: Crypto.versioned_hash(), list(Node.t())) ::
@spec fetch_transaction(address :: Crypto.versioned_hash()) ::
Transaction.t() | nil
def fetch_transaction(address, node_list) when is_binary(address) do
storage_nodes = Election.chain_storage_nodes(address, node_list)
def fetch_transaction(address) when is_binary(address) do
storage_nodes = Election.chain_storage_nodes(address, P2P.authorized_and_available_nodes())

case TransactionChain.fetch_transaction_remotely(address, storage_nodes) do
{:ok, tx} ->
Expand Down Expand Up @@ -50,11 +52,11 @@ defmodule Archethic.Replication.TransactionContext do
@doc """
Fetch the transaction inputs for a transaction address at a given time
"""
@spec fetch_transaction_inputs(address :: Crypto.versioned_hash(), DateTime.t(), list(Node.t())) ::
@spec fetch_transaction_inputs(address :: Crypto.versioned_hash(), DateTime.t()) ::
list(TransactionInput.t())
def fetch_transaction_inputs(address, timestamp = %DateTime{}, node_list)
def fetch_transaction_inputs(address, timestamp = %DateTime{})
when is_binary(address) do
storage_nodes = Election.chain_storage_nodes(address, node_list)
storage_nodes = Election.chain_storage_nodes(address, P2P.authorized_and_available_nodes())

address
|> TransactionChain.stream_inputs_remotely(storage_nodes, timestamp)
Expand Down
39 changes: 9 additions & 30 deletions lib/archethic/replication/transaction_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,19 @@ defmodule Archethic.Replication.TransactionValidator do
@spec validate(
validated_transaction :: Transaction.t(),
previous_transaction :: Transaction.t() | nil,
inputs_outputs :: list(TransactionInput.t()),
self_repair? :: boolean()
inputs_outputs :: list(TransactionInput.t())
) ::
:ok | {:error, error()}
def validate(
tx = %Transaction{},
previous_transaction,
inputs,
self_repair?
) do
with :ok <- valid_transaction(tx, inputs, true, self_repair?),
:ok <- validate_inheritance(tx, previous_transaction, self_repair?) do
def validate(tx = %Transaction{}, previous_transaction, inputs) do
with :ok <- valid_transaction(tx, inputs, true),
:ok <- validate_inheritance(tx, previous_transaction) do
validate_chain(tx, previous_transaction)
end
end

defp validate_inheritance(_, _, true), do: :ok

defp validate_inheritance(
tx = %Transaction{validation_stamp: %ValidationStamp{timestamp: timestamp}},
prev_tx,
false
prev_tx
) do
if Contracts.accept_new_contract?(prev_tx, tx, timestamp) do
:ok
Expand All @@ -96,11 +87,11 @@ defmodule Archethic.Replication.TransactionValidator do
This function called by the replication nodes which are involved in the chain storage
"""
@spec validate(Transaction.t(), boolean()) :: :ok | {:error, error()}
def validate(tx = %Transaction{}, self_repair? \\ false),
do: valid_transaction(tx, [], false, self_repair?)
@spec validate(Transaction.t()) :: :ok | {:error, error()}
def validate(tx = %Transaction{}),
do: valid_transaction(tx, [], false)

defp valid_transaction(tx = %Transaction{}, inputs, chain_node?, false) when is_list(inputs) do
defp valid_transaction(tx = %Transaction{}, inputs, chain_node?) when is_list(inputs) do
with :ok <- validate_consensus(tx),
:ok <- validate_validation_stamp(tx) do
if chain_node? do
Expand All @@ -115,18 +106,6 @@ defmodule Archethic.Replication.TransactionValidator do
end
end

defp valid_transaction(tx = %Transaction{}, _, _, true) do
with :ok <- validate_consensus(tx),
:ok <- validate_node_election(tx),
:ok <- validate_no_additional_error(tx) do
:ok
else
{:error, _} = e ->
# TODO: start malicious detection
e
end
end

defp validate_consensus(
tx = %Transaction{
cross_validation_stamps: cross_stamps
Expand Down
9 changes: 4 additions & 5 deletions lib/archethic/self_repair/repair_worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,12 @@ defmodule Archethic.SelfRepair.RepairWorker do
timeout,
acceptance_resolver
) do
# TODO: Also download replication attestation from beacon nodes to ensure validity of the transaction
if storage? do
case Replication.validate_and_store_transaction_chain(tx, true, authorized_nodes) do
:ok -> SelfRepair.update_last_address(address, authorized_nodes)
error -> error
end
:ok = Replication.sync_transaction_chain(tx, authorized_nodes, true)
SelfRepair.update_last_address(address, authorized_nodes)
else
Replication.validate_and_store_transaction(tx, true)
Replication.synchronize_io_transaction(tx, true)
end
else
true ->
Expand Down
9 changes: 2 additions & 7 deletions test/archethic/replication/transaction_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ defmodule Archethic.Replication.TransactionContextTest do
authorization_date: DateTime.utc_now()
})

assert %Transaction{} =
TransactionContext.fetch_transaction("@Alice1", P2P.authorized_and_available_nodes())
assert %Transaction{} = TransactionContext.fetch_transaction("@Alice1")
end

test "stream_transaction_chain/1 should retrieve the previous transaction chain" do
Expand Down Expand Up @@ -152,10 +151,6 @@ defmodule Archethic.Replication.TransactionContextTest do
})

assert [%TransactionInput{from: "@Bob3", amount: 19_300_000, type: :UCO}] =
TransactionContext.fetch_transaction_inputs(
"@Alice1",
DateTime.utc_now(),
P2P.authorized_and_available_nodes()
)
TransactionContext.fetch_transaction_inputs("@Alice1", DateTime.utc_now())
end
end
18 changes: 9 additions & 9 deletions test/archethic/replication/transaction_validator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ defmodule Archethic.Replication.TransactionValidatorTest do
}}
end

describe "validate/2" do
describe "validate/1" do
test "should return {:error, :invalid_atomic_commitment} when the atomic commitment is not reached" do
unspent_outputs = [
%UnspentOutput{
Expand All @@ -78,31 +78,31 @@ defmodule Archethic.Replication.TransactionValidatorTest do

assert {:error, :invalid_atomic_commitment} =
TransactionFactory.create_transaction_with_not_atomic_commitment(unspent_outputs)
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end

test "should return {:error, :invalid_proof_of_work} when an invalid proof of work" do
assert {:error, :invalid_proof_of_work} =
TransactionFactory.create_transaction_with_invalid_proof_of_work()
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end

test "should return {:error, :invalid_node_election} when the validation stamp signature is invalid" do
assert {:error, :invalid_node_election} =
TransactionFactory.create_transaction_with_invalid_validation_stamp_signature()
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end

test "should return {:error, :invalid_transaction_fee} when the fees are invalid" do
assert {:error, :invalid_transaction_fee} =
TransactionFactory.create_transaction_with_invalid_fee()
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end

test "should return {:error, :invalid_transaction_movements} when the transaction movements are invalid" do
assert {:error, :invalid_transaction_movements} =
TransactionFactory.create_transaction_with_invalid_transaction_movements()
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end

test "should return {:error, :invalid_transaction_with_inconsistencies} when there is an atomic commitment but with inconsistencies" do
Expand All @@ -117,7 +117,7 @@ defmodule Archethic.Replication.TransactionValidatorTest do

assert {:error, :invalid_transaction_with_inconsistencies} =
TransactionFactory.create_valid_transaction_with_inconsistencies(unspent_outputs)
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end

test "should return :ok when the transaction is valid" do
Expand All @@ -132,7 +132,7 @@ defmodule Archethic.Replication.TransactionValidatorTest do

assert :ok =
TransactionFactory.create_valid_transaction(unspent_outputs)
|> TransactionValidator.validate(false)
|> TransactionValidator.validate()
end
end

Expand All @@ -149,7 +149,7 @@ defmodule Archethic.Replication.TransactionValidatorTest do

assert :ok =
TransactionFactory.create_valid_transaction(unspent_outputs)
|> TransactionValidator.validate(nil, unspent_outputs, false)
|> TransactionValidator.validate(nil, unspent_outputs)
end
end
end

0 comments on commit e974dde

Please sign in to comment.