Skip to content

Commit

Permalink
Add a threshold date for v2 calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
Neylix committed Oct 19, 2022
1 parent 8a7f051 commit 0adceb2
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 47 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 @@ -179,7 +179,7 @@ defmodule Archethic.Bootstrap.NetworkInit do

operations =
%LedgerOperations{
fee: Mining.get_transaction_fee(tx, 0.07),
fee: Mining.get_transaction_fee(tx, 0.07, timestamp),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.from_transaction(tx, timestamp)
Expand Down
7 changes: 5 additions & 2 deletions lib/archethic/contracts/worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -442,15 +442,18 @@ defmodule Archethic.Contracts.Worker do

%{uco: uco_balance, token: token_balances} = Account.get_balance(contract_address)

now = DateTime.utc_now()

uco_usd_price =
DateTime.utc_now()
now
|> OracleChain.get_uco_price()
|> Keyword.get(:usd)

tx_fee =
Mining.get_transaction_fee(
next_transaction,
uco_usd_price
uco_usd_price,
now
)

with true <- uco_balance > uco_to_transfer + tx_fee,
Expand Down
4 changes: 2 additions & 2 deletions lib/archethic/mining.ex
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,6 @@ defmodule Archethic.Mining do
@doc """
Get the transaction fee
"""
@spec get_transaction_fee(Transaction.t(), float()) :: non_neg_integer()
defdelegate get_transaction_fee(tx, uco_price_in_usd), to: Fee, as: :calculate
@spec get_transaction_fee(Transaction.t(), float(), DateTime.t()) :: non_neg_integer()
defdelegate get_transaction_fee(tx, uco_price_in_usd, timestamp), to: Fee, as: :calculate
end
30 changes: 24 additions & 6 deletions lib/archethic/mining/fee.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,32 @@ defmodule Archethic.Mining.Fee do

@unit_uco 100_000_000

# TODO : remove before mainnet launch
# Threashold for v2 application at ~U[2022-10-25 04:57:46Z]
@v2_threshold_date 1_666_666_666

@doc """
Determine the fee to paid for the given transaction
The fee will differ according to the transaction type and complexity
Genesis, network and wallet transaction cost nothing.
"""
@spec calculate(transaction :: Transaction.t(), uco_usd_price :: float()) :: non_neg_integer()
def calculate(%Transaction{type: :keychain}, _), do: 0
def calculate(%Transaction{type: :keychain_access}, _), do: 0
@spec calculate(
transaction :: Transaction.t(),
uco_usd_price :: float(),
timestamp :: DateTime.t()
) :: non_neg_integer()
def calculate(%Transaction{type: :keychain}, _, _), do: 0
def calculate(%Transaction{type: :keychain_access}, _, _), do: 0

def calculate(
tx = %Transaction{
address: address,
type: type
},
uco_price_in_usd
uco_price_in_usd,
timestamp
) do
cond do
address == Bootstrap.genesis_address() ->
Expand All @@ -44,7 +53,7 @@ defmodule Archethic.Mining.Fee do

true ->
nb_recipients = get_number_recipients(tx)
nb_bytes = get_transaction_size(tx)
nb_bytes = get_transaction_size(tx, DateTime.to_unix(timestamp))
nb_storage_nodes = get_number_replicas(tx)

# TODO: determine the fee for smart contract execution
Expand Down Expand Up @@ -87,7 +96,16 @@ defmodule Archethic.Mining.Fee do

defp get_additional_fee(_tx, _uco_price_usd), do: 0

defp get_transaction_size(%Transaction{data: tx_data}) do
defp get_transaction_size(tx = %Transaction{}, timestamp)
when timestamp <= @v2_threshold_date do
tx
|> Transaction.to_pending()
|> Transaction.serialize()
|> byte_size()
end

defp get_transaction_size(%Transaction{data: tx_data}, timestamp)
when timestamp > @v2_threshold_date do
tx_data
|> TransactionData.serialize()
|> byte_size()
Expand Down
6 changes: 4 additions & 2 deletions lib/archethic/mining/validation_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,8 @@ defmodule Archethic.Mining.ValidationContext do
fee =
Fee.calculate(
tx,
usd_price
usd_price,
validation_time
)

resolved_movements =
Expand Down Expand Up @@ -1046,7 +1047,8 @@ defmodule Archethic.Mining.ValidationContext do
) do
Fee.calculate(
tx,
OracleChain.get_uco_price(timestamp) |> Keyword.fetch!(:usd)
OracleChain.get_uco_price(timestamp) |> Keyword.fetch!(:usd),
timestamp
) == fee
end

Expand Down
2 changes: 1 addition & 1 deletion lib/archethic/replication/transaction_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ defmodule Archethic.Replication.TransactionValidator do
|> OracleChain.get_uco_price()
|> Keyword.fetch!(:usd)

Mining.get_transaction_fee(tx, uco_price_usd)
Mining.get_transaction_fee(tx, uco_price_usd, timestamp)
end

defp validate_transaction_movements(
Expand Down
6 changes: 4 additions & 2 deletions lib/archethic_web/controllers/api/transaction_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,17 @@ defmodule ArchethicWeb.API.TransactionController do
def transaction_fee(conn, tx) do
case TransactionPayload.changeset(tx) do
changeset = %{valid?: true} ->
uco_price = OracleChain.get_uco_price(DateTime.utc_now())
now = DateTime.utc_now()

uco_price = OracleChain.get_uco_price(now)
uco_eur = uco_price |> Keyword.fetch!(:eur)
uco_usd = uco_price |> Keyword.fetch!(:usd)

fee =
changeset
|> TransactionPayload.to_map()
|> Transaction.cast()
|> Mining.get_transaction_fee(uco_usd)
|> Mining.get_transaction_fee(uco_usd, now)

conn
|> put_status(:ok)
Expand Down
22 changes: 11 additions & 11 deletions test/archethic/mining/fee_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(0.2)
|> Fee.calculate(0.2, DateTime.from_unix!(1_666_666_667))
end

test "should increase fee when the amount increases for single transfer " do
Expand All @@ -65,7 +65,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0)
|> Fee.calculate(2.0, DateTime.from_unix!(1_666_666_667))

# 0.00501425 UCO for 60 UCO
assert 501_425 =
Expand All @@ -88,7 +88,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0)
|> Fee.calculate(2.0, DateTime.from_unix!(1_666_666_667))
end

test "should decrease the fee when the amount stays the same but the price of UCO increases" do
Expand All @@ -113,7 +113,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0)
|> Fee.calculate(2.0, DateTime.from_unix!(1_666_666_667))

# 0.00100285 UCO for 1 UCO at $10.0
assert 100_285 =
Expand All @@ -136,7 +136,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(10.0)
|> Fee.calculate(10.0, DateTime.from_unix!(1_666_666_667))
end

test "sending multiple transfers should cost more than sending a single big transfer" do
Expand All @@ -161,7 +161,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(0.2)
|> Fee.calculate(0.2, DateTime.from_unix!(1_666_666_667))

# 500.1525425 UCO for 1000 transfer of 1 UCO
assert 50_015_254_250 =
Expand All @@ -185,7 +185,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(0.2)
|> Fee.calculate(0.2, DateTime.from_unix!(1_666_666_667))
end

test "should increase the fee when the transaction size increases" do
Expand All @@ -201,7 +201,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(0.2)
|> Fee.calculate(0.2, DateTime.from_unix!(1_666_666_667))

# 25.05004 UCO to store 10MB
assert 2_505_004_000 =
Expand All @@ -215,7 +215,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(0.2)
|> Fee.calculate(0.2, DateTime.from_unix!(1_666_666_667))
end

test "should cost more with more replication nodes" do
Expand All @@ -240,7 +240,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0)
|> Fee.calculate(2.0, DateTime.from_unix!(1_666_666_667))

add_nodes(100)

Expand All @@ -265,7 +265,7 @@ defmodule Archethic.Mining.FeeTest do
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0)
|> Fee.calculate(2.0, DateTime.from_unix!(1_666_666_667))
end
end

Expand Down
10 changes: 5 additions & 5 deletions test/archethic/mining/validation_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ defmodule Archethic.Mining.ValidationContextTest do
proof_of_election: Election.validation_nodes_election_seed_sorting(tx, DateTime.utc_now()),
ledger_operations:
%LedgerOperations{
fee: Fee.calculate(tx, 0.07),
fee: Fee.calculate(tx, 0.07, DateTime.utc_now()),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.from_transaction(tx, timestamp)
Expand All @@ -232,7 +232,7 @@ defmodule Archethic.Mining.ValidationContextTest do
proof_of_election: Election.validation_nodes_election_seed_sorting(tx, DateTime.utc_now()),
ledger_operations:
%LedgerOperations{
fee: Fee.calculate(tx, 0.07),
fee: Fee.calculate(tx, 0.07, DateTime.utc_now()),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.from_transaction(tx, timestamp)
Expand Down Expand Up @@ -266,7 +266,7 @@ defmodule Archethic.Mining.ValidationContextTest do
validation_time: timestamp,
unspent_outputs: unspent_outputs
}) do
fee = Fee.calculate(tx, 0.07)
fee = Fee.calculate(tx, 0.07, DateTime.utc_now())

%ValidationStamp{
timestamp: timestamp,
Expand Down Expand Up @@ -304,7 +304,7 @@ defmodule Archethic.Mining.ValidationContextTest do
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),
fee: Fee.calculate(tx, 0.07, DateTime.utc_now()),
transaction_movements: Transaction.get_movements(tx),
unspent_outputs: [
%UnspentOutput{
Expand All @@ -331,7 +331,7 @@ defmodule Archethic.Mining.ValidationContextTest do
proof_of_election: Election.validation_nodes_election_seed_sorting(tx, DateTime.utc_now()),
ledger_operations:
%LedgerOperations{
fee: Fee.calculate(tx, 0.07),
fee: Fee.calculate(tx, 0.07, DateTime.utc_now()),
transaction_movements: Transaction.get_movements(tx)
}
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs, timestamp),
Expand Down
2 changes: 1 addition & 1 deletion test/archethic/replication_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ defmodule Archethic.ReplicationTest do

ledger_operations =
%LedgerOperations{
fee: Fee.calculate(tx, 0.07)
fee: Fee.calculate(tx, 0.07, DateTime.utc_now())
}
|> LedgerOperations.consume_inputs(tx.address, unspent_outputs, timestamp)

Expand Down
28 changes: 20 additions & 8 deletions test/archethic_web/controllers/api/transaction_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ defmodule ArchethicWeb.API.TransactionControllerTest do

describe "transaction_fee/2" do
test "should send ok response and return fee for valid transaction body", %{conn: conn} do
MemTable.add_oracle_data("uco", %{"eur" => 0.2, "usd" => 0.2}, DateTime.utc_now())
now = DateTime.utc_now()
MemTable.add_oracle_data("uco", %{"eur" => 0.2, "usd" => 0.2}, now)

conn =
post(conn, "/api/transaction_fee", %{
Expand Down Expand Up @@ -53,13 +54,24 @@ defmodule ArchethicWeb.API.TransactionControllerTest do
"version" => 1
})

assert %{
"fee" => 5_000_290,
"rates" => %{
"eur" => 0.2,
"usd" => 0.2
}
} = json_response(conn, 200)
# Condition for v2 calculation
if DateTime.to_unix(now) <= 1_666_666_666 do
assert %{
"fee" => 5_001_344,
"rates" => %{
"eur" => 0.2,
"usd" => 0.2
}
} = json_response(conn, 200)
else
assert %{
"fee" => 5_000_290,
"rates" => %{
"eur" => 0.2,
"usd" => 0.2
}
} = json_response(conn, 200)
end
end

test "should send bad_request response for invalid transaction body", %{conn: conn} do
Expand Down
Loading

0 comments on commit 0adceb2

Please sign in to comment.