Skip to content

Commit

Permalink
add_get_first_address_in_smart_contract_library_#740
Browse files Browse the repository at this point in the history
  • Loading branch information
apoorv-2204 authored and Neylix committed Jan 25, 2023
1 parent e1cf03d commit a6bce72
Show file tree
Hide file tree
Showing 14 changed files with 568 additions and 38 deletions.
8 changes: 8 additions & 0 deletions lib/archethic/contracts/interpreter/condition.ex
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ defmodule Archethic.Contracts.ConditionInterpreter do
{node, acc}
end

# Whitelist the get_first_transaction_address/0 function in condition
defp prewalk(
node = {{:atom, "get_first_transaction_address"}, _, []},
acc = {:ok, %{scope: {:condition, _, _}}}
) do
{node, acc}
end

# Whitelist the get_genesis_public_key/0 function in condition
defp prewalk(
node = {{:atom, "get_genesis_public_key"}, _, []},
Expand Down
68 changes: 38 additions & 30 deletions lib/archethic/contracts/interpreter/library.ex
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
defmodule Archethic.Contracts.Interpreter.Library do
@moduledoc false

alias Archethic.Election

alias Archethic.P2P
alias Archethic.P2P.Message.GetGenesisAddress
alias Archethic.P2P.Message.GetFirstPublicKey
alias Archethic.P2P.Message.GenesisAddress
alias Archethic.P2P.Message.FirstPublicKey
alias Archethic.{
Election,
P2P,
P2P.Message.GetFirstPublicKey,
P2P.Message.FirstPublicKey,
TransactionChain
}

@doc """
Match a regex expression
Expand Down Expand Up @@ -181,19 +181,6 @@ defmodule Archethic.Contracts.Interpreter.Library do
def size(list) when is_list(list), do: length(list)
def size(map) when is_map(map), do: map_size(map)

@doc """
Get the genesis address of the chain
"""
@spec get_genesis_address(binary()) ::
binary()
def get_genesis_address(address) do
bin_address = decode_binary(address)
nodes = Election.chain_storage_nodes(bin_address, P2P.authorized_and_available_nodes())
{:ok, address} = download_first_address(nodes, bin_address)
Base.encode16(address)
end

@doc """
Get the genesis public key
"""
Expand All @@ -215,22 +202,13 @@ defmodule Archethic.Contracts.Interpreter.Library do

defp download_first_public_key([], _address), do: {:error, :network_issue}

defp download_first_address([node | rest], address) do
case P2P.send_message(node, %GetGenesisAddress{address: address}) do
{:ok, %GenesisAddress{address: address}} -> {:ok, address}
{:error, _} -> download_first_address(rest, address)
end
end

defp download_first_address([], _address), do: {:error, :network_issue}

@doc """
Return the current UNIX timestamp
"""
@spec timestamp() :: non_neg_integer()
def timestamp, do: DateTime.utc_now() |> DateTime.to_unix()

defp decode_binary(bin) do
def decode_binary(bin) do
if String.printable?(bin) do
case Base.decode16(bin, case: :mixed) do
{:ok, hex} ->
Expand All @@ -243,4 +221,34 @@ defmodule Archethic.Contracts.Interpreter.Library do
bin
end
end

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

case TransactionChain.fetch_genesis_address_remotely(addr_bin, nodes) do
{:ok, genesis_address} -> Base.encode16(genesis_address)
{:error, reason} -> raise "[get_genesis_address] #{inspect(reason)}"
end
end

@doc """
Get the First transaction address of the transaction chain for the given address
"""
@spec get_first_transaction_address(address :: binary()) ::
binary()
def get_first_transaction_address(address) do
addr_bin = decode_binary(address)
nodes = Election.chain_storage_nodes(address, P2P.authorized_and_available_nodes())

case TransactionChain.fetch_first_transaction_address_remotely(addr_bin, nodes) do
{:ok, first_transaction_address} -> Base.encode16(first_transaction_address)
{:error, reason} -> raise "[get_first_transaction_address] #{inspect(reason)}"
end
end
end
9 changes: 9 additions & 0 deletions lib/archethic/contracts/interpreter/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ defmodule Archethic.Contracts.Interpreter.Utils do
{node, acc}
end

# Whitelist the get_first_address/1 function
def prewalk(
node = {{:atom, "get_first_transaction_address"}, _, [_address]},
acc = {:ok, %{scope: scope}}
)
when scope != :root do
{node, acc}
end

# Whitelist the get_genesis_public_key/1 function
def prewalk(
node = {{:atom, "get_genesis_public_key"}, _, [_address]},
Expand Down
7 changes: 4 additions & 3 deletions lib/archethic/p2p/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ defmodule Archethic.P2P.Message do
GetTransactionChainLength,
GetTransactionInputs,
GetUnspentOutputs,
GetFirstTransactionAddress,
FirstTransactionAddress,
LastTransactionAddress,
ListNodes,
NewBeaconSlot,
Expand Down Expand Up @@ -100,6 +102,8 @@ defmodule Archethic.P2P.Message do
| GetBalance.t()
| GetTransactionInputs.t()
| GetTransactionChainLength.t()
| GetFirstTransactionAddress.t()
| FirstTransactionAddress.t()
| NotifyEndOfNodeSync.t()
| GetLastTransactionAddress.t()
| NotifyLastTransactionAddress.t()
Expand Down Expand Up @@ -200,12 +204,9 @@ defmodule Archethic.P2P.Message do

@doc """
Serialize a message into binary
## Examples
iex> Message.encode(%Ok{})
<<254>>
iex> %Message.GetTransaction{
...> address: <<0, 40, 71, 99, 6, 218, 243, 156, 193, 63, 176, 168, 22, 226, 31, 170, 119, 122,
...> 13, 188, 75, 49, 171, 219, 222, 133, 86, 132, 188, 206, 233, 66, 7>>
Expand Down
45 changes: 45 additions & 0 deletions lib/archethic/p2p/message/first_transaction_address.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
defmodule Archethic.P2P.Message.FirstTransactionAddress do
@moduledoc false
alias Archethic.Utils
@enforce_keys [:address]
defstruct [:address]

@type t() :: %__MODULE__{
address: binary()
}

@doc """
Serialize FirstTransactionAddress Struct
iex> %FirstTransactionAddress{
...> address: <<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
...> 3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
...> } |> FirstTransactionAddress.serialize()
#address
<<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
"""
def serialize(%__MODULE__{address: address}) do
<<address::binary>>
end

@doc """
DeSerialize FirstTransactionAddress Struct
iex> # First address
...> <<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
...> 3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
...> |> FirstTransactionAddress.deserialize()
{
%FirstTransactionAddress{
address: <<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
}, ""}
"""
def deserialize(bin) do
{address, <<rest::bitstring>>} = Utils.deserialize_address(bin)

{%__MODULE__{address: address}, rest}
end
end
65 changes: 65 additions & 0 deletions lib/archethic/p2p/message/get_first_transaction_address.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
defmodule Archethic.P2P.Message.GetFirstTransactionAddress do
@moduledoc """
Represents a message to request the first address from a transaction chain.
Genesis address != first transaction address
Hash of current index public key gives current index address
Hash of genesis public key gives genesis address
Hash of first public key gives first transaction address
"""
alias Archethic.Utils
alias Archethic.P2P.Message.FirstTransactionAddress
alias Archethic.TransactionChain
alias Archethic.P2P.Message.NotFound

@enforce_keys [:address]
defstruct [:address]

@type t() :: %__MODULE__{
address: binary()
}

@doc """
Serialize GetFirstTransactionAddress Struct
iex> %GetFirstTransactionAddress{
...> address: <<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
...> 3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
...> } |> GetFirstTransactionAddress.serialize()
#address
<<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
"""
def serialize(%__MODULE__{address: address}) do
<<address::binary>>
end

@doc """
DeSerialize GetFirstTransactionAddress Struct
iex> # First address
...> <<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
...> 3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
...> |> GetFirstTransactionAddress.deserialize()
{
%GetFirstTransactionAddress{
address: <<0, 0, 94, 5, 249, 103, 126, 31, 43, 57, 25, 14, 187, 133, 59, 234, 201, 172,
3, 195, 43, 81, 81, 146, 164, 202, 147, 218, 207, 204, 31, 185, 73, 251>>
}, ""}
"""
def deserialize(bin) do
{address, <<rest::bitstring>>} = Utils.deserialize_address(bin)

{%__MODULE__{address: address}, rest}
end

def process(%__MODULE__{address: address}) do
case TransactionChain.get_first_transaction_address(address) do
{:error, :transaction_not_exists} ->
%NotFound{}

{:ok, first_address} ->
%FirstTransactionAddress{address: first_address}
end
end
end
5 changes: 4 additions & 1 deletion lib/archethic/p2p/message/message_id.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ defmodule Archethic.P2P.MessageId do
GetTransactionChainLength,
GetP2PView,
GetFirstPublicKey,
GetFirstTransactionAddress,
FirstTransactionAddress,
NotifyLastTransactionAddress,
GetTransactionSummary,
Ping,
Expand Down Expand Up @@ -102,7 +104,7 @@ defmodule Archethic.P2P.MessageId do
GetLastTransactionAddress => 21,
NotifyLastTransactionAddress => 22,
GetTransactionSummary => 23,
# id 24 is available for a new message
GetFirstTransactionAddress => 24,
Ping => 25,
GetBeaconSummary => 26,
NewBeaconSlot => 27,
Expand All @@ -119,6 +121,7 @@ defmodule Archethic.P2P.MessageId do
NotifyReplicationValidation => 38,

# Responses
FirstTransactionAddress => 228,
AddressList => 229,
ShardRepair => 230,
SummaryAggregate => 231,
Expand Down
43 changes: 41 additions & 2 deletions lib/archethic/transaction_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ defmodule Archethic.TransactionChain do
TransactionChainLength,
TransactionInputList,
TransactionList,
UnspentOutputList
UnspentOutputList,
GetFirstTransactionAddress,
FirstTransactionAddress
}

alias __MODULE__.MemTables.KOLedger
Expand Down Expand Up @@ -262,6 +264,24 @@ defmodule Archethic.TransactionChain do
get_transaction(address, fields)
end

@doc """
Get the first transaction Address from a genesis/chain address
"""
@spec get_first_transaction_address(address :: binary()) ::
{:ok, address :: binary()} | {:error, :transaction_not_exists}
def get_first_transaction_address(address) when is_binary(address) do
address =
address
|> get_genesis_address()
|> list_chain_addresses()
|> Enum.at(0)

case address do
nil -> {:error, :transaction_not_exists}
{address, _datetime} -> {:ok, address}
end
end

@doc """
Get the genesis address from a given chain address
"""
Expand Down Expand Up @@ -981,7 +1001,7 @@ defmodule Archethic.TransactionChain do

@doc """
Retrieve the genesis address for a chain from P2P Quorom
It queries the the network for genesis address
It queries the the network for genesis address.
"""
@spec fetch_genesis_address_remotely(address :: binary(), list(Node.t())) ::
{:ok, binary()} | {:error, :network_issue}
Expand All @@ -994,4 +1014,23 @@ defmodule Archethic.TransactionChain do
{:error, :network_issue}
end
end

@doc """
Retrieve the First transaction address for a chain from P2P Quorom
"""
@spec fetch_first_transaction_address_remotely(address :: binary(), nodes :: list(Node.t())) ::
{:ok, binary()} | {:error, :network_issue} | {:error, :does_not_exist}
def fetch_first_transaction_address_remotely(address, nodes)
when is_binary(address) and is_list(nodes) do
case P2P.quorum_read(nodes, %GetFirstTransactionAddress{address: address}) do
{:ok, %NotFound{}} ->
{:error, :does_not_exist}

{:ok, %FirstTransactionAddress{address: first_address}} ->
{:ok, first_address}

_ ->
{:error, :network_issue}
end
end
end
Loading

0 comments on commit a6bce72

Please sign in to comment.