Skip to content

Commit

Permalink
Merge branch 'develop' into improve_balance_api
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelmanzanera committed Feb 1, 2023
2 parents afb3341 + 8563823 commit 3b5867e
Show file tree
Hide file tree
Showing 25 changed files with 813 additions and 199 deletions.
23 changes: 16 additions & 7 deletions lib/archethic/bootstrap/transaction_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ defmodule Archethic.Bootstrap.TransactionHandler do
@doc """
Send a transaction to the network towards a welcome node
"""
@spec send_transaction(Transaction.t(), list(Node.t())) :: :ok | {:error, :network_issue}
@spec send_transaction(Transaction.t(), list(Node.t())) ::
:ok
def send_transaction(tx = %Transaction{address: address}, nodes) do
Logger.info("Send node transaction...",
transaction_address: Base.encode16(address),
Expand All @@ -32,6 +33,8 @@ defmodule Archethic.Bootstrap.TransactionHandler do
do_send_transaction(nodes, tx)
end

@spec do_send_transaction(list(Node.t()), Transaction.t()) ::
:ok
defp do_send_transaction([node | rest], tx) do
case P2P.send_message(node, %NewTransaction{transaction: tx}) do
{:ok, %Ok{}} ->
Expand All @@ -48,7 +51,13 @@ defmodule Archethic.Bootstrap.TransactionHandler do
)
|> Enum.reject(&(&1.first_public_key == Crypto.first_node_public_key()))

await_confirmation(tx.address, storage_nodes)
case await_confirmation(tx.address, storage_nodes) do
:ok ->
:ok

{:error, :network_issue} ->
raise("No node responded with confirmation for new Node tx")
end

{:error, _} = e ->
Logger.error("Cannot send node transaction - #{inspect(e)}",
Expand All @@ -61,14 +70,12 @@ defmodule Archethic.Bootstrap.TransactionHandler do

defp do_send_transaction([], _), do: {:error, :network_issue}

@spec await_confirmation(tx_address :: binary(), list(Node.t())) ::
:ok | {:error, :network_issue}
defp await_confirmation(tx_address, [node | rest]) do
case P2P.send_message(node, %GetTransactionSummary{address: tx_address}) do
{:ok,
%TransactionSummaryMessage{
transaction_summary: %TransactionSummary{
address: ^tx_address
}
}} ->
%TransactionSummaryMessage{transaction_summary: %TransactionSummary{address: ^tx_address}}} ->
:ok

{:ok, %NotFound{}} ->
Expand All @@ -84,6 +91,8 @@ defmodule Archethic.Bootstrap.TransactionHandler do
end
end

defp await_confirmation(_, []), do: {:error, :network_issue}

@doc """
Create a new node transaction
"""
Expand Down
143 changes: 63 additions & 80 deletions lib/archethic/contracts/interpreter/action.ex
Original file line number Diff line number Diff line change
Expand Up @@ -146,132 +146,109 @@ defmodule Archethic.Contracts.ActionInterpreter do
{node, acc}
end

# Whitelist the add_uco_transfer function parameters
# Blacklist the add_uco_transfer argument list
defp prewalk(
node = {{:atom, "to"}, address},
acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
_acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
)
when is_binary(address) do
{node, acc}
end

defp prewalk(
node = {{:atom, "to"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
) do
{node, acc}
when not is_tuple(address) and not is_binary(address) do
throw({:error, "invalid add_uco_transfer arguments", node})
end

defp prewalk(
node = {{:atom, "amount"}, amount},
acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
_acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
)
when is_integer(amount) and amount > 0 do
{node, acc}
when (not is_tuple(amount) and not is_integer(amount)) or amount <= 0 do
throw({:error, "invalid add_uco_transfer arguments", node})
end

defp prewalk(
node = {{:atom, "amount"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
) do
{node, acc}
node = {{:atom, atom}, _},
_acc = {:ok, %{scope: {:function, "add_uco_transfer", {:actions, _}}}}
)
when atom != "to" and atom != "amount" do
throw({:error, "invalid add_uco_transfer arguments", node})
end

# Whitelist the add_token_transfer argument list
# Blacklist the add_token_transfer argument list
defp prewalk(
node = {{:atom, "to"}, address},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
_acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
)
when is_binary(address) do
{node, acc}
end

defp prewalk(
node = {{:atom, "to"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
) do
{node, acc}
when not is_tuple(address) and not is_binary(address) do
throw({:error, "invalid add_token_transfer arguments", node})
end

defp prewalk(
node = {{:atom, "amount"}, amount},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
_acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
)
when is_integer(amount) and amount > 0 do
{node, acc}
when (not is_tuple(amount) and not is_integer(amount)) or amount <= 0 do
throw({:error, "invalid add_token_transfer arguments", node})
end

defp prewalk(
node = {{:atom, "amount"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
) do
{node, acc}
end

defp prewalk(
node = {{:atom, "token_address"}, token_address},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
node = {{:atom, "token_address"}, address},
_acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
)
when is_binary(token_address) do
{node, acc}
when not is_tuple(address) and not is_binary(address) do
throw({:error, "invalid add_token_transfer arguments", node})
end

defp prewalk(
node = {{:atom, "token_address"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
) do
{node, acc}
end

defp prewalk(
node = {{:atom, "token_id"}, token_id},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
node = {{:atom, "token_id"}, id},
_acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
)
when is_integer(token_id) and token_id >= 0 do
{node, acc}
when (not is_tuple(id) and not is_integer(id)) or id < 0 do
throw({:error, "invalid add_token_transfer arguments", node})
end

defp prewalk(
node = {{:atom, "token_id"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
) do
{node, acc}
node = {{:atom, atom}, _},
_acc = {:ok, %{scope: {:function, "add_token_transfer", {:actions, _}}}}
)
when atom != "to" and atom != "amount" and atom != "token_address" and atom != "token_id" do
throw({:error, "invalid add_token_transfer arguments", node})
end

# Whitelist the add_ownership argument list
# Blacklist the add_ownership argument list
defp prewalk(
node = {{:atom, "secret"}, secret},
acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
_acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
)
when is_binary(secret) do
{node, acc}
when not is_tuple(secret) and not is_binary(secret) do
throw({:error, "invalid add_ownership arguments", node})
end

defp prewalk(
node = {{:atom, "secret"}, {{:atom, _}, _, _}},
acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
) do
{node, acc}
node = {{:atom, "secret_key"}, secret_key},
_acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
)
when not is_tuple(secret_key) and not is_binary(secret_key) do
throw({:error, "invalid add_ownership arguments", node})
end

defp prewalk(
node = {{:atom, "secret_key"}, _secret_key},
acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
) do
{node, acc}
node = {{:atom, "authorized_public_keys"}, authorized_public_keys},
_acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
)
when not is_tuple(authorized_public_keys) and not is_list(authorized_public_keys) do
throw({:error, "invalid add_ownership arguments", node})
end

defp prewalk(
node = {{:atom, "authorized_public_keys"}, authorized_public_keys},
acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
node = {{:atom, atom}, _},
_acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
)
when is_list(authorized_public_keys) do
{node, acc}
when atom != "secret" and atom != "secret_key" and atom != "authorized_public_keys" do
throw({:error, "invalid add_ownership arguments", node})
end

# Whitelist the keywords
defp prewalk(
node = {{:atom, "authorized_public_keys"}, {{:atom, _, _}}},
acc = {:ok, %{scope: {:function, "add_ownership", {:actions, _}}}}
node = {{:atom, _}, _},
acc = {:ok, _}
) do
{node, acc}
end
Expand Down Expand Up @@ -341,18 +318,24 @@ defmodule Archethic.Contracts.ActionInterpreter do
end

@doc """
Execute actions code and returns a transaction as result
Execute actions code and returns either the next transaction or nil
"""
@spec execute(Macro.t(), map()) :: Transaction.t()
@spec execute(Macro.t(), map()) :: Transaction.t() | nil
def execute(code, constants \\ %{}) do
{%{"next_transaction" => next_transaction}, _} =
result =
Code.eval_quoted(code,
scope:
Map.put(constants, "next_transaction", %Transaction{
data: %TransactionData{}
})
)

next_transaction
case result do
{%{"next_transaction" => next_transaction}, _} ->
next_transaction

_ ->
nil
end
end
end
56 changes: 49 additions & 7 deletions lib/archethic/contracts/interpreter/library.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ defmodule Archethic.Contracts.Interpreter.Library do
TransactionChain,
Contracts.ContractConstants,
Contracts.TransactionLookup,
Contracts.Interpreter.Utils
Utils
}

alias Archethic.Contracts.Interpreter.Utils, as: SCUtils

require Logger

@doc """
Match a regex expression
Expand Down Expand Up @@ -141,7 +145,7 @@ defmodule Archethic.Contracts.Interpreter.Library do
:blake2b
end

:crypto.hash(algo, Utils.maybe_decode_hex(content))
:crypto.hash(algo, SCUtils.maybe_decode_hex(content))
|> Base.encode16()
end

Expand Down Expand Up @@ -180,7 +184,7 @@ defmodule Archethic.Contracts.Interpreter.Library do
2
"""
@spec size(binary() | list()) :: non_neg_integer()
def size(binary) when is_binary(binary), do: binary |> Utils.maybe_decode_hex() |> byte_size()
def size(binary) when is_binary(binary), do: binary |> SCUtils.maybe_decode_hex() |> byte_size()
def size(list) when is_list(list), do: length(list)
def size(map) when is_map(map), do: map_size(map)

Expand All @@ -192,7 +196,7 @@ defmodule Archethic.Contracts.Interpreter.Library do
@spec get_calls(binary()) :: list(map())
def get_calls(contract_address) do
contract_address
|> Utils.maybe_decode_hex()
|> SCUtils.maybe_decode_hex()
|> TransactionLookup.list_contract_transactions()
|> Enum.map(fn {address, _, _} ->
# TODO: parallelize
Expand All @@ -206,7 +210,7 @@ defmodule Archethic.Contracts.Interpreter.Library do
"""
@spec get_genesis_public_key(binary()) :: binary()
def get_genesis_public_key(address) do
bin_address = Utils.maybe_decode_hex(address)
bin_address = SCUtils.maybe_decode_hex(address)
nodes = Election.chain_storage_nodes(bin_address, P2P.authorized_and_available_nodes())
{:ok, key} = download_first_public_key(nodes, bin_address)
Base.encode16(key)
Expand All @@ -228,13 +232,51 @@ defmodule Archethic.Contracts.Interpreter.Library do
@spec timestamp() :: non_neg_integer()
def timestamp, do: DateTime.utc_now() |> DateTime.to_unix()

@doc """
Provide a token id which uniquely identify the token base on it's properties and genesis address.
"""
@spec get_token_id(binary()) :: {:error, binary()} | {:ok, binary()}
def get_token_id(address) do
address = SCUtils.get_address(address, :get_token_id)
t1 = Task.async(fn -> Archethic.fetch_genesis_address_remotely(address) end)
t2 = Task.async(fn -> Archethic.search_transaction(address) end)

with {:ok, {:ok, genesis_address}} <- Task.yield(t1),
{:ok, {:ok, tx}} <- Task.yield(t2),
{:ok, %{id: id}} <- Utils.get_token_properties(genesis_address, tx) do
id
else
{:ok, {:error, :network_issue}} ->
{:error, "Network issue"}

{:ok, {:error, :transaction_not_exists}} ->
{:error, "Transaction not exists"}

{:ok, {:error, :transaction_invalid}} ->
{:error, "Transaction invalid"}

{:error, :decode_error} ->
{:error, "Error in decoding transaction"}

{:error, :not_a_token_transaction} ->
{:error, "Transaction is not of type token"}

{:exit, reason} ->
Logger.debug("Task exited with reason #{inspect(reason)}")
{:error, "Task Exited!"}

nil ->
{:error, "Task didn't responded within timeout!"}
end
end

@doc """
Get the genesis address of the chain
"""
@spec get_genesis_address(binary()) ::
binary()
def get_genesis_address(address) do
addr_bin = Utils.maybe_decode_hex(address)
addr_bin = SCUtils.maybe_decode_hex(address)
nodes = Election.chain_storage_nodes(address, P2P.authorized_and_available_nodes())

case TransactionChain.fetch_genesis_address_remotely(addr_bin, nodes) do
Expand All @@ -249,7 +291,7 @@ defmodule Archethic.Contracts.Interpreter.Library do
@spec get_first_transaction_address(address :: binary()) ::
binary()
def get_first_transaction_address(address) do
addr_bin = Utils.maybe_decode_hex(address)
addr_bin = SCUtils.maybe_decode_hex(address)
nodes = Election.chain_storage_nodes(address, P2P.authorized_and_available_nodes())

case TransactionChain.fetch_first_transaction_address_remotely(addr_bin, nodes) do
Expand Down
Loading

0 comments on commit 3b5867e

Please sign in to comment.