Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AEIP2 update #580

Merged
Merged
8 changes: 2 additions & 6 deletions lib/archethic/mining/fee.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,16 @@ defmodule Archethic.Mining.Fee do
) do
with {:ok, json} <- Jason.decode(content),
"non-fungible" <- Map.get(json, "type", "fungible"),
utxos = [_ | _] <- Map.get(json, "properties", []) do
utxos when is_list(utxos) <- Map.get(json, "collection") do
samuelmanzanera marked this conversation as resolved.
Show resolved Hide resolved
nb_utxos = length(utxos)
base_fee = minimum_fee(uco_price_in_usd)
(:math.log10(nb_utxos) + 1) * nb_utxos * base_fee
else
{:error, _} ->
0

"fungible" ->
_ ->
1 * minimum_fee(uco_price_in_usd)

[] ->
# Invalid non-fungible definition
0
end
end

Expand Down
51 changes: 46 additions & 5 deletions lib/archethic/mining/pending_transaction_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ defmodule Archethic.Mining.PendingTransactionValidation do
alias Archethic.Governance.Code.Proposal, as: CodeProposal
require Logger

@unit_uco 100_000_000

@doc """
Determines if the transaction is accepted into the network
"""
Expand Down Expand Up @@ -615,22 +617,61 @@ defmodule Archethic.Mining.PendingTransactionValidation do

with {:ok, json_token} <- Jason.decode(content),
:ok <- ExJsonSchema.Validator.validate(schema, json_token),
%{"type" => "non-fungible", "supply" => supply, "properties" => properties}
when length(properties) == supply / 100_000_000 <- json_token do
%{
"type" => "non-fungible",
"supply" => supply,
"collection" => collection
} <- json_token,
{:decimals, 8} <- {:decimals, Map.get(json_token, "decimals", 8)},
{:length, ^supply} <- {:length, length(collection) * @unit_uco},
{:id, true} <- {:id, valid_collection_id?(collection)} do
:ok
else
{:error, reason} ->
{:error, "Invalid token transaction - Invalid specification #{inspect(reason)}"}

%{"type" => "fungible", "properties" => properties} when length(properties) > 1 ->
{:error, "Invalid token transaction - Fungible should have only 1 set of properties"}
%{"type" => "fungible", "collection" => _collection} ->
{:error, "Invalid token transaction - Fungible should not have collection attribute"}

%{"type" => "fungible"} ->
:ok

%{"type" => "non-fungible", "supply" => supply} when supply != @unit_uco ->
{:error,
"Invalid token transaction - Non fungible should have collection attribute or supply should be #{@unit_uco}"}

%{"type" => "non-fungible"} ->
:ok

{:decimals, _} ->
{:error, "Invalid token transaction - Non fungible should have 8 decimals"}

{:length, _} ->
{:error,
"Invalid token transaction - Supply should match collection for non-fungible tokens"}

{:id, false} ->
{:error,
"Invalid token transaction - Supply should match properties for non-fungible tokens"}
"Invalid token transaction - Specified id must be different for all item in the collection"}
end
end

defp valid_collection_id?(collection) do
# If an id is specified in an item of the collection,
# all items must have a different specified id
if Enum.at(collection, 0) |> Map.has_key?("id") do
Enum.reduce_while(collection, MapSet.new(), fn properties, acc ->
id = Map.get(properties, "id")

if id != nil && !MapSet.member?(acc, id) do
{:cont, MapSet.put(acc, id)}
else
{:halt, MapSet.new()}
end
end)
|> MapSet.size() > 0
else
Enum.all?(collection, &(!Map.has_key?(&1, "id")))
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/archethic/p2p/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ defmodule Archethic.P2P.Message do
token_balances_binary =
token_balances
|> Enum.reduce([], fn {{token_address, token_id}, amount}, acc ->
[<<token_address::binary, amount::float, token_id::8>> | acc]
[<<token_address::binary, amount::float, VarInt.from_value(token_id)::binary>> | acc]
end)
|> Enum.reverse()
|> :erlang.list_to_binary()
Expand Down Expand Up @@ -1201,7 +1201,7 @@ defmodule Archethic.P2P.Message do

defp deserialize_token_balances(rest, nb_token_balances, acc) do
{token_address, <<amount::float, rest::bitstring>>} = Utils.deserialize_address(rest)
<<token_id::8, rest::bitstring>> = rest
{token_id, rest} = VarInt.get_value(rest)

deserialize_token_balances(
rest,
Expand Down Expand Up @@ -1305,7 +1305,7 @@ defmodule Archethic.P2P.Message do

%{utxos: utxos, offset: offset, more?: more?} =
utxos
|> Enum.sort_by(& &1.timestamp, {:desc, DateTime})
# |> Enum.sort_by(& &1.timestamp, {:desc, DateTime})
|> Enum.with_index()
|> Enum.drop(offset)
|> Enum.reduce_while(%{utxos: [], offset: 0, more?: false}, fn {utxo, index}, acc ->
Expand Down
4 changes: 2 additions & 2 deletions lib/archethic/transaction_chain/transaction/data/ledger.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ defmodule Archethic.TransactionChain.TransactionData.Ledger do
# TOKEN transfer amount
0, 0, 0, 0, 62, 149, 186, 128,
# TOKEN ID
0
1, 0
>>
"""
@spec serialize(t()) :: binary()
Expand All @@ -81,7 +81,7 @@ defmodule Archethic.TransactionChain.TransactionData.Ledger do
...> 122, 206, 185, 71, 140, 74, 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175,
...> 0, 0, 59, 140, 2, 130, 52, 88, 206, 176, 29, 10, 173, 95, 179, 27, 166, 66, 52,
...> 165, 11, 146, 194, 246, 89, 73, 85, 202, 120, 242, 136, 136, 63, 53,
...> 0, 0, 0, 0, 62, 149, 186, 128, 0>>
...> 0, 0, 0, 0, 62, 149, 186, 128, 1, 0>>
...> |> Ledger.deserialize()
{
%Ledger{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ defmodule Archethic.TransactionChain.TransactionData.TokenLedger do
# Token amount
0, 0, 0, 0, 62, 149, 186, 128,
# Token_ID
0
1, 0
>>
"""
@spec serialize(t()) :: binary()
Expand All @@ -61,7 +61,7 @@ defmodule Archethic.TransactionChain.TransactionData.TokenLedger do
iex> <<1, 1, 0, 0, 49, 101, 72, 154, 152, 3, 174, 47, 2, 35, 7, 92, 122, 206, 185, 71, 140, 74,
...> 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175, 0, 0, 59, 140, 2, 130, 52, 88, 206, 176, 29, 10, 173, 95, 179, 27, 166, 66, 52,
...> 165, 11, 146, 194, 246, 89, 73, 85, 202, 120, 242, 136, 136, 63, 53,
...> 0, 0, 0, 0, 62, 149, 186, 128, 0>>
...> 0, 0, 0, 0, 62, 149, 186, 128, 1, 0>>
...> |> TokenLedger.deserialize()
{
%TokenLedger{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Archethic.TransactionChain.TransactionData.TokenLedger.Transfer do
defstruct [:to, :amount, :token_address, conditions: [], token_id: 0]

alias Archethic.Utils
alias Archethic.Utils.VarInt
# impl token_id

@typedoc """
Expand Down Expand Up @@ -47,11 +48,11 @@ defmodule Archethic.TransactionChain.TransactionData.TokenLedger.Transfer do
# Transfer amount
0, 0, 0, 0, 62, 149, 186, 128,
# Token ID
0
1, 0
>>
"""
def serialize(%__MODULE__{token_address: token, to: to, amount: amount, token_id: token_id}) do
<<token::binary, to::binary, amount::64, token_id::8>>
<<token::binary, to::binary, amount::64, VarInt.from_value(token_id)::binary>>
end

@doc """
Expand All @@ -64,7 +65,7 @@ defmodule Archethic.TransactionChain.TransactionData.TokenLedger.Transfer do
...> 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175,
...> 0, 0, 104, 134, 142, 120, 40, 59, 99, 108, 63, 166, 143, 250, 93, 186, 216, 117,
...> 85, 106, 43, 26, 120, 35, 44, 137, 243, 184, 160, 251, 223, 0, 93, 14,
...> 0, 0, 0, 0, 62, 149, 186, 128, 0>>
...> 0, 0, 0, 0, 62, 149, 186, 128, 1, 0>>
...> |> Transfer.deserialize()
{
%Transfer{
Expand All @@ -82,7 +83,7 @@ defmodule Archethic.TransactionChain.TransactionData.TokenLedger.Transfer do
def deserialize(data) do
{token_address, rest} = Utils.deserialize_address(data)
{recipient_address, <<amount::64, rest::bitstring>>} = Utils.deserialize_address(rest)
<<token_id::8, rest::bitstring>> = rest
{token_id, rest} = VarInt.get_value(rest)

{
%__MODULE__{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
...> %Transaction{
...> address: "@Token2",
...> type: :token,
...> data: %TransactionData{content: "{\"supply\": 1000000000, \"type\": \"non-fungible\", \"properties\": [[],[],[],[],[],[],[],[],[],[]]}"}
...> data: %TransactionData{content: "{\"supply\": 1000000000, \"type\": \"non-fungible\", \"collection\": [{},{},{},{},{},{},{},{},{},{}]}"}
...> }
...> )
%LedgerOperations{
Expand All @@ -78,6 +78,44 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
%UnspentOutput{from: "@Token2", amount: 100_000_000, type: {:token, "@Token2", 10}}
]
}

iex> LedgerOperations.from_transaction(%LedgerOperations{},
...> %Transaction{
...> address: "@Token2",
...> type: :token,
...> data: %TransactionData{content: "{\"supply\": 100000000, \"type\": \"non-fungible\"}"}
...> }
...> )
%LedgerOperations{
unspent_outputs: [
%UnspentOutput{from: "@Token2", amount: 100_000_000, type: {:token, "@Token2", 1}},
]
}

iex> LedgerOperations.from_transaction(%LedgerOperations{},
...> %Transaction{
...> address: "@Token2",
...> type: :token,
...> data: %TransactionData{content: "{\"supply\": 200000000, \"type\": \"non-fungible\", \"collection\": [{\"id\": 42}, {\"id\": 38}]}"}
...> }
...> )
%LedgerOperations{
unspent_outputs: [
%UnspentOutput{from: "@Token2", amount: 100_000_000, type: {:token, "@Token2", 42}},
%UnspentOutput{from: "@Token2", amount: 100_000_000, type: {:token, "@Token2", 38}}
]
}

iex> LedgerOperations.from_transaction(%LedgerOperations{},
...> %Transaction{
...> address: "@Token2",
...> type: :token,
...> data: %TransactionData{content: "{\"supply\": 1000000000, \"type\": \"non-fungible\", \"collection\": [{}]}"}
...> }
...> )
%LedgerOperations{
unspent_outputs: []
}
"""
@spec from_transaction(t(), Transaction.t()) :: t()
def from_transaction(ops = %__MODULE__{}, %Transaction{
Expand Down Expand Up @@ -109,15 +147,26 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
end

defp get_token_utxos(
%{"type" => "non-fungible", "supply" => supply, "properties" => properties},
%{"type" => "non-fungible", "supply" => supply, "collection" => collection},
address
)
when length(properties) == supply / @unit_uco do
properties
|> Enum.with_index()
|> Enum.map(fn {_item_properties, index} ->
%UnspentOutput{from: address, amount: 1 * @unit_uco, type: {:token, address, index + 1}}
end)
) do
if length(collection) == supply / @unit_uco do
collection
|> Enum.with_index()
|> Enum.map(fn {item_properties, index} ->
token_id = Map.get(item_properties, "id", index + 1)
%UnspentOutput{from: address, amount: 1 * @unit_uco, type: {:token, address, token_id}}
end)
else
[]
end
end

defp get_token_utxos(
%{"type" => "non-fungible", "supply" => @unit_uco},
address
) do
[%UnspentOutput{from: address, amount: 1 * @unit_uco, type: {:token, address, 1}}]
end

defp get_token_utxos(_, _), do: []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
0, 49, 101, 72, 154, 152, 3, 174, 47, 2, 35, 7, 92, 122, 206, 185, 71, 140, 74,
197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175,
# Token ID
0
1, 0
>>
"""
@spec serialize(t()) :: <<_::64, _::_*8>>
Expand Down Expand Up @@ -92,7 +92,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
iex> <<0, 0, 214, 107, 17, 107, 227, 11, 17, 43, 204, 48, 78, 129, 145, 126, 45, 68, 194,
...> 159, 19, 92, 240, 29, 37, 105, 183, 232, 56, 42, 163, 236, 251, 186,
...> 0, 0, 0, 0, 1, 201, 195, 128, 1, 0, 0, 49, 101, 72, 154, 152, 3, 174, 47, 2, 35, 7, 92, 122, 206, 185, 71, 140, 74,
...> 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175, 0
...> 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175, 1, 0
...> >>
...> |> TransactionMovement.deserialize()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation

alias Archethic.Crypto
alias Archethic.Utils
alias Archethic.Utils.VarInt

@typedoc """
Transaction movement can be:
Expand All @@ -16,14 +17,14 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
def serialize(:UCO), do: <<0>>

def serialize({:token, address, token_id}) do
<<1::8, address::binary, token_id::8>>
<<1::8, address::binary, VarInt.from_value(token_id)::binary>>
end

def deserialize(<<0::8, rest::bitstring>>), do: {:UCO, rest}

def deserialize(<<1::8, rest::bitstring>>) do
{address, rest} = Utils.deserialize_address(rest)
<<token_id::8, rest::bitstring>> = rest
{token_id, rest} = VarInt.get_value(rest)
{{:token, address, token_id}, rest}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
0, 49, 101, 72, 154, 152, 3, 174, 47, 2, 35, 7, 92, 122, 206, 185, 71, 140, 74,
197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175,
# Token ID
0
1, 0
>>
"""
@spec serialize(__MODULE__.t()) :: <<_::64, _::_*8>>
Expand Down Expand Up @@ -93,7 +93,7 @@ defmodule Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperation
iex> <<0, 0, 214, 107, 17, 107, 227, 11, 17, 43, 204, 48, 78, 129, 145, 126, 45, 68, 194,
...> 159, 19, 92, 240, 29, 37, 105, 183, 232, 56, 42, 163, 236, 251, 186,
...> 0, 0, 0, 0, 62, 149, 186, 128, 1, 0, 0, 49, 101, 72, 154, 152, 3, 174, 47, 2, 35, 7, 92, 122, 206, 185, 71, 140, 74,
...> 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175, 0>>
...> 197, 46, 99, 117, 89, 96, 100, 20, 0, 34, 181, 215, 143, 175, 1, 0>>
...> |> UnspentOutput.deserialize()
{
%UnspentOutput{
Expand Down
6 changes: 6 additions & 0 deletions lib/archethic_web/graphql_schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ defmodule ArchethicWeb.GraphQLSchema do
end)
end

@desc """
Query the network to get the storage nonce public key
"""
field :shared_secrets, :shared_secrets do
resolve(fn _, _ ->
{:ok, Resolver.shared_secrets()}
Expand Down Expand Up @@ -147,6 +150,9 @@ defmodule ArchethicWeb.GraphQLSchema do
end)
end

@desc """
Query the network to get the value of an oracle at a specific time or the last value
"""
field :oracle_data, :oracle_data do
arg(:timestamp, :timestamp)

Expand Down
Loading