Skip to content

Commit

Permalink
Include UCO transfer towards burning address (#483)
Browse files Browse the repository at this point in the history
The fee burn have been removed from the transaction movements
and the burn is included as input.

This allows also the display of all the transactions targeting the
burning address.
  • Loading branch information
Samuel authored and Samuel committed Aug 17, 2022
1 parent 7b24e90 commit ac8a646
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 84 deletions.
11 changes: 9 additions & 2 deletions lib/archethic/account/mem_tables_loader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ defmodule Archethic.Account.MemTablesLoader do
validation_stamp: %ValidationStamp{
timestamp: timestamp,
ledger_operations: %LedgerOperations{
fee: fee,
unspent_outputs: unspent_outputs,
transaction_movements: transaction_movements
}
Expand All @@ -79,6 +80,14 @@ defmodule Archethic.Account.MemTablesLoader do
:ok = set_transaction_movements(address, transaction_movements, timestamp, tx_type)
:ok = set_unspent_outputs(address, unspent_outputs, timestamp)

if fee > 0 do
UCOLedger.add_unspent_output(
LedgerOperations.burning_address(),
%UnspentOutput{from: address, amount: fee, type: :UCO},
timestamp
)
end

Logger.info("Loaded into in memory account tables",
transaction_address: Base.encode16(address),
transaction_type: tx_type
Expand All @@ -98,10 +107,8 @@ defmodule Archethic.Account.MemTablesLoader do
end

defp set_transaction_movements(address, transaction_movements, timestamp, tx_type) do
# address is sender to is reciever
transaction_movements
|> Enum.filter(&(&1.amount > 0))
|> Enum.reject(&(&1.to == LedgerOperations.burning_address()))
|> Enum.reduce(%{}, &aggregate_movements(&1, &2, address, tx_type))
|> Enum.each(fn
{{to, :uco}, utxo} ->
Expand Down
1 change: 0 additions & 1 deletion lib/archethic/bootstrap/network_init.ex
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ defmodule Archethic.Bootstrap.NetworkInit do
fee: Mining.get_transaction_fee(tx, 0.07),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.add_burning_movement()
|> LedgerOperations.from_transaction(tx)
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs)

Expand Down
2 changes: 2 additions & 0 deletions lib/archethic/mining/distributed_workflow.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ defmodule Archethic.Mining.DistributedWorkflow do

alias Archethic.TransactionChain
alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperations
alias Archethic.TransactionChain.Transaction.CrossValidationStamp
alias Archethic.TransactionChain.Transaction.ValidationStamp
alias Archethic.TransactionChain.TransactionSummary
Expand Down Expand Up @@ -175,6 +176,7 @@ defmodule Archethic.Mining.DistributedWorkflow do
else
resolved_addresses
|> Enum.map(fn {_origin, resolved} -> resolved end)
|> Enum.concat([LedgerOperations.burning_address()])
|> Election.io_storage_nodes(authorized_nodes)
end

Expand Down
2 changes: 2 additions & 0 deletions lib/archethic/mining/standalone_workflow.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ defmodule Archethic.Mining.StandaloneWorkflow do

alias Archethic.TransactionChain
alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperations
alias Archethic.TransactionChain.TransactionSummary

require Logger
Expand Down Expand Up @@ -72,6 +73,7 @@ defmodule Archethic.Mining.StandaloneWorkflow do
else
resolved_addresses
|> Enum.map(fn {_origin, resolved} -> resolved end)
|> Enum.concat([LedgerOperations.burning_address()])
|> Election.io_storage_nodes(authorized_nodes)
end

Expand Down
22 changes: 13 additions & 9 deletions lib/archethic/mining/validation_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -765,9 +765,11 @@ defmodule Archethic.Mining.ValidationContext do
fee: fee,
transaction_movements: resolved_movements
}
|> LedgerOperations.add_burning_movement()
|> LedgerOperations.from_transaction(tx)
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs),
|> LedgerOperations.consume_inputs(
tx.address,
unspent_outputs
),
recipients: resolved_recipients,
errors: [initial_error, chain_error(prev_tx, tx)] |> Enum.filter(& &1)
}
Expand Down Expand Up @@ -1068,7 +1070,7 @@ defmodule Archethic.Mining.ValidationContext do
defp valid_stamp_transaction_movements?(
%ValidationStamp{
ledger_operations:
ops = %LedgerOperations{
_ops = %LedgerOperations{
transaction_movements: transaction_movements
}
},
Expand All @@ -1077,13 +1079,11 @@ defmodule Archethic.Mining.ValidationContext do
initial_movements =
tx
|> Transaction.get_movements()
|> Enum.map(&{&1.to, &1})
|> Enum.map(&{{&1.to, &1.type}, &1})
|> Enum.into(%{})

tx_burn_mvt = LedgerOperations.get_burning_movement(ops)

resolved_movements =
Enum.reduce(resolved_addresses, [tx_burn_mvt], fn {to, resolved}, acc ->
Enum.reduce(resolved_addresses, [], fn {to, resolved}, acc ->
case Map.get(initial_movements, to) do
nil ->
acc
Expand All @@ -1093,7 +1093,8 @@ defmodule Archethic.Mining.ValidationContext do
end
end)

Enum.all?(resolved_movements, &(&1 in transaction_movements))
length(resolved_movements) == length(transaction_movements) and
Enum.all?(resolved_movements, &(&1 in transaction_movements))
end

defp valid_stamp_unspent_outputs?(
Expand All @@ -1111,7 +1112,10 @@ defmodule Archethic.Mining.ValidationContext do
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.from_transaction(tx)
|> LedgerOperations.consume_inputs(tx.address, previous_unspent_outputs)
|> LedgerOperations.consume_inputs(
tx.address,
previous_unspent_outputs
)

expected_unspent_outputs == next_unspent_outputs
end
Expand Down
24 changes: 12 additions & 12 deletions lib/archethic/replication/transaction_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,11 @@ defmodule Archethic.Replication.TransactionValidator do
initial_movements =
tx
|> Transaction.get_movements()
|> Enum.map(&{&1.to, &1})
|> Enum.map(&{{&1.to, &1.type}, &1})
|> Enum.into(%{})

tx_burn_mvt = LedgerOperations.get_burning_movement(ops)

resolved_movements =
Enum.reduce(resolved_addresses, [tx_burn_mvt], fn {to, resolved}, acc ->
Enum.reduce(resolved_addresses, [], fn {to, resolved}, acc ->
case Map.get(initial_movements, to) do
nil ->
acc
Expand All @@ -285,16 +283,18 @@ defmodule Archethic.Replication.TransactionValidator do
end
end)

if Enum.all?(resolved_movements, &(&1 in transaction_movements)) do
with true <- length(resolved_movements) == length(transaction_movements),
true <- Enum.all?(resolved_movements, &(&1 in transaction_movements)) do
:ok
else
Logger.error(
"Invalid movements: #{inspect(ops.transaction_movements)}",
transaction_address: Base.encode16(tx.address),
transaction_type: tx.type
)

{:error, :invalid_transaction_movements}
false ->
Logger.error(
"Invalid movements: #{inspect(ops.transaction_movements)}",
transaction_address: Base.encode16(tx.address),
transaction_type: tx.type
)

{:error, :invalid_transaction_movements}
end
end

Expand Down
9 changes: 8 additions & 1 deletion lib/archethic/transaction_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ defmodule Archethic.TransactionChain do
alias __MODULE__.Transaction
alias __MODULE__.TransactionData
alias __MODULE__.Transaction.ValidationStamp

alias __MODULE__.Transaction.ValidationStamp.LedgerOperations.TransactionMovement.Type,
as: TransactionMovementType

alias __MODULE__.Transaction.ValidationStamp.LedgerOperations.UnspentOutput
alias __MODULE__.TransactionSummary
alias __MODULE__.TransactionInput
Expand Down Expand Up @@ -489,7 +493,10 @@ defmodule Archethic.TransactionChain do
Resolve all the last addresses from the transaction data
"""
@spec resolve_transaction_addresses(Transaction.t(), DateTime.t()) ::
list({origin_address :: binary(), resolved_address :: binary()})
list(
{{origin_address :: binary(), type :: TransactionMovementType.t()},
resolved_address :: binary()}
)
def resolve_transaction_addresses(
tx = %Transaction{data: %TransactionData{recipients: recipients}},
time = %DateTime{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
:token => %{binary() => non_neg_integer()}
}
def total_to_spend(%__MODULE__{transaction_movements: transaction_movements, fee: fee}) do
transaction_movements
|> Enum.reject(&(&1.to == @burning_address))
|> ledger_balances(%{uco: fee, token: %{}})
ledger_balances(transaction_movements, %{uco: fee, token: %{}})
end

defp ledger_balances(movements, acc \\ %{uco: 0, token: %{}}) do
Expand Down Expand Up @@ -424,9 +422,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
def movement_addresses(%__MODULE__{
transaction_movements: transaction_movements
}) do
transaction_movements
|> Enum.reject(&(&1.to == @burning_address))
|> Enum.map(& &1.to)
Enum.map(transaction_movements, & &1.to)
end

@doc """
Expand Down Expand Up @@ -597,25 +593,4 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
fee: fee
}
end

@doc """
Add the movement to burn the fee
"""
@spec add_burning_movement(t()) :: t()
def add_burning_movement(ops = %__MODULE__{}) do
burn_movement = get_burning_movement(ops)
Map.update(ops, :transaction_movements, [burn_movement], &([burn_movement] ++ &1))
end

@doc """
Get the burning transaction movement
"""
@spec get_burning_movement(t()) :: TransactionMovement.t()
def get_burning_movement(%__MODULE__{fee: fee}) do
%TransactionMovement{
to: @burning_address,
amount: fee,
type: :UCO
}
end
end
13 changes: 10 additions & 3 deletions lib/archethic_web/templates/explorer/transaction_details.html.leex
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,18 @@
</div>

<div :class=" tab_panel == 'tx' ? '' : 'is-hidden' ">
<%= if assigns[:error] != nil and @error == :not_exists do %>
<%= cond do %>

<% @address == burning_address() -> %>
<p class="subtitle is-size-5">This address is not owned by any user being the burn address</p>

<% assigns[:error] != nil and @error == :not_exists -> %>
<p>The transaction does not exists yet. </p>
<hr />
<div class="mt-4 box has-background-warning-light">
<small>It may appear later. <br />Please retry when the transaction will be processed.</small>
</div>
<% else %>
<% true -> %>
<div>
<div class="columns mt-4">
<div class="column is-2">
Expand Down Expand Up @@ -398,7 +403,9 @@
<%= to_float(@transaction.validation_stamp.ledger_operations.fee) %>
<span class="tag is-primary is-light ml-2">UCO</span>
<%= if @transaction.validation_stamp.ledger_operations.fee > 0 do %>
(<%= format_full_usd_amount(@transaction.validation_stamp.ledger_operations.fee, @uco_price_at_time[:usd], @uco_price_now[:usd]) %>)
(<%= format_full_usd_amount(@transaction.validation_stamp.ledger_operations.fee, @uco_price_at_time[:usd], @uco_price_now[:usd]) %>)
<p class="is-size-7 mt-2">The fees have been transfered to the
<%= link to: Routes.live_path(@socket, ArchethicWeb.TransactionDetailsLive, Base.encode16(Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperations.burning_address())) do %><span>burning address</span><% end %></p>
<% end %>
</p>
</div>
Expand Down
3 changes: 3 additions & 0 deletions lib/archethic_web/views/explorer_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule ArchethicWeb.ExplorerView do
alias Archethic.P2P.Node

alias Archethic.TransactionChain.TransactionSummary
alias Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperations

alias Archethic.Utils

Expand Down Expand Up @@ -248,4 +249,6 @@ defmodule ArchethicWeb.ExplorerView do

{family, key, key_certificate}
end

def burning_address, do: LedgerOperations.burning_address()
end
7 changes: 1 addition & 6 deletions test/archethic/bootstrap/network_init_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,12 @@ defmodule Archethic.Bootstrap.NetworkInitTest do

tx_fee = tx.validation_stamp.ledger_operations.fee
unspent_output = 1_000_000_000_000 - (tx_fee + 500_000_000_000)
burning_address = LedgerOperations.burning_address()

assert %Transaction{
validation_stamp: %ValidationStamp{
ledger_operations: %LedgerOperations{
fee: ^tx_fee,
transaction_movements: [
%TransactionMovement{
to: ^burning_address,
amount: ^tx_fee,
type: :UCO
},
%TransactionMovement{to: "@Alice2", amount: 500_000_000_000, type: :UCO}
],
unspent_outputs: [
Expand Down
28 changes: 11 additions & 17 deletions test/archethic/mining/validation_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ defmodule Archethic.Mining.ValidationContextTest do
fee: Fee.calculate(tx, 0.07),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.add_burning_movement()
|> LedgerOperations.from_transaction(tx)
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs),
signature: :crypto.strong_rand_bytes(32)
Expand All @@ -225,7 +224,6 @@ defmodule Archethic.Mining.ValidationContextTest do
fee: Fee.calculate(tx, 0.07),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.add_burning_movement()
|> LedgerOperations.from_transaction(tx)
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs)
}
Expand All @@ -246,7 +244,6 @@ defmodule Archethic.Mining.ValidationContextTest do
fee: 2_020_000_000,
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.add_burning_movement()
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs)
}
|> ValidationStamp.sign()
Expand Down Expand Up @@ -291,19 +288,17 @@ defmodule Archethic.Mining.ValidationContextTest do
proof_of_work: Crypto.origin_node_public_key(),
proof_of_integrity: TransactionChain.proof_of_integrity([tx]),
proof_of_election: Election.validation_nodes_election_seed_sorting(tx, DateTime.utc_now()),
ledger_operations:
%LedgerOperations{
fee: Fee.calculate(tx, 0.07),
transaction_movements: Transaction.get_movements(tx),
unspent_outputs: [
%UnspentOutput{
amount: 100_000_000_000,
from: tx.address,
type: :UCO
}
]
}
|> LedgerOperations.add_burning_movement()
ledger_operations: %LedgerOperations{
fee: Fee.calculate(tx, 0.07),
transaction_movements: Transaction.get_movements(tx),
unspent_outputs: [
%UnspentOutput{
amount: 100_000_000_000,
from: tx.address,
type: :UCO
}
]
}
}
|> ValidationStamp.sign()
end
Expand All @@ -322,7 +317,6 @@ defmodule Archethic.Mining.ValidationContextTest do
fee: Fee.calculate(tx, 0.07),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.add_burning_movement()
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs),
errors: [:contract_validation]
}
Expand Down
1 change: 0 additions & 1 deletion test/archethic/replication_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ defmodule Archethic.ReplicationTest do
fee: Fee.calculate(tx, 0.07)
}
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs)
|> LedgerOperations.add_burning_movement()

validation_stamp =
%ValidationStamp{
Expand Down
Loading

0 comments on commit ac8a646

Please sign in to comment.