Skip to content

Commit

Permalink
Add timestamp function in smart contract library (#612)
Browse files Browse the repository at this point in the history
* Add timestamp function in the SC library

* Add timestamp in the SC interpreter constants

* Add timestamp in the conditions

* Fix chain of SC
  • Loading branch information
samuelmanzanera authored Oct 18, 2022
1 parent 63eae26 commit 6eff994
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 29 deletions.
7 changes: 5 additions & 2 deletions lib/archethic/contracts/contract/conditions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defmodule Archethic.Contracts.Contract.Conditions do
:uco_transfers,
:token_transfers,
:previous_public_key,
:timestamp,
origin_family: :all
]

Expand All @@ -27,7 +28,8 @@ defmodule Archethic.Contracts.Contract.Conditions do
uco_transfers: map() | Macro.t() | nil,
token_transfers: map() | Macro.t() | nil,
previous_public_key: binary() | Macro.t() | nil,
origin_family: SharedSecrets.origin_family() | :all
origin_family: SharedSecrets.origin_family() | :all,
timestamp: DateTime.t() | nil
}

def empty?(%__MODULE__{
Expand All @@ -38,7 +40,8 @@ defmodule Archethic.Contracts.Contract.Conditions do
secrets: nil,
uco_transfers: nil,
token_transfers: nil,
previous_public_key: nil
previous_public_key: nil,
timestamp: nil
}),
do: true

Expand Down
15 changes: 13 additions & 2 deletions lib/archethic/contracts/contract/constants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule Archethic.Contracts.Contract.Constants do
alias Archethic.TransactionChain.TransactionData.Ownership
alias Archethic.TransactionChain.TransactionData.UCOLedger
alias Archethic.TransactionChain.TransactionData.UCOLedger.Transfer, as: UCOTransfer
alias Archethic.TransactionChain.Transaction.ValidationStamp

@doc """
Extract constants from a transaction into a map
Expand All @@ -40,7 +41,8 @@ defmodule Archethic.Contracts.Contract.Constants do
}
},
recipients: recipients
}
},
validation_stamp: validation_stamp
}) do
%{
"address" => address,
Expand Down Expand Up @@ -75,7 +77,16 @@ defmodule Archethic.Contracts.Contract.Constants do
}

Map.update(acc, to, [token_transfer], &[token_transfer | &1])
end)
end),
"timestamp" =>
case validation_stamp do
# Happens during the transaction validation
nil ->
nil

%ValidationStamp{timestamp: timestamp} ->
DateTime.to_unix(timestamp)
end
}
end

Expand Down
24 changes: 24 additions & 0 deletions lib/archethic/contracts/interpreter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,11 @@ defmodule Archethic.Contracts.Interpreter do
{node, acc}
end

# Whitelist the timestamp/0 function
defp prewalk(node = {{:atom, "timestamp"}, _, []}, acc = {:ok, %{scope: scope}})
when scope != :root,
do: {node, acc}

defp prewalk(
node = {{:atom, "get_genesis_public_key"}, _, [_address]},
acc = {:ok, %{scope: scope}}
Expand Down Expand Up @@ -664,6 +669,10 @@ defmodule Archethic.Contracts.Interpreter do
{node, acc}
end

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

# Whitelist the used of functions in the actions
defp prewalk(node = {{:atom, fun_name}, _, _}, {:ok, acc = %{scope: :actions}})
when fun_name in @transaction_statements_functions_names,
Expand Down Expand Up @@ -1436,6 +1445,21 @@ defmodule Archethic.Contracts.Interpreter do
{:previous_public_key, true}
end

defp validate_condition({:timestamp, nil}, _) do
# Skip the verification as timestamp change for each transaction
{:timestamp, true}
end

defp validate_condition({:type, nil}, %{"next" => %{"type" => "transfer"}}) do
# Skip the verification when it's the default type
{:type, true}
end

defp validate_condition({:content, nil}, %{"next" => %{"content" => ""}}) do
# Skip the verification when it's the default type
{:content, true}
end

# Validation rules for inherit constraints
defp validate_condition({field, nil}, %{"previous" => prev, "next" => next}) do
{field, Map.get(prev, Atom.to_string(field)) == Map.get(next, Atom.to_string(field))}
Expand Down
6 changes: 6 additions & 0 deletions lib/archethic/contracts/interpreter/library.ex
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,10 @@ defmodule Archethic.Contracts.Interpreter.Library do
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()
end
20 changes: 2 additions & 18 deletions lib/archethic/contracts/worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,6 @@ defmodule Archethic.Contracts.Worker do
%{next_transaction: next_tx, previous_transaction: prev_tx}
|> chain_type()
|> chain_code()
|> chain_content()
|> chain_ownerships()

case get_transaction_seed(prev_tx) do
Expand Down Expand Up @@ -379,10 +378,10 @@ defmodule Archethic.Contracts.Worker do
defp chain_type(
acc = %{
next_transaction: %Transaction{type: nil},
previous_transaction: %Transaction{type: previous_type}
previous_transaction: _
}
) do
put_in(acc, [:next_transaction, :type], previous_type)
put_in(acc, [:next_transaction, Access.key(:type)], :transfer)
end

defp chain_type(acc), do: acc
Expand All @@ -398,21 +397,6 @@ defmodule Archethic.Contracts.Worker do

defp chain_code(acc), do: acc

defp chain_content(
acc = %{
next_transaction: %Transaction{data: %TransactionData{content: ""}},
previous_transaction: %Transaction{data: %TransactionData{content: previous_content}}
}
) do
put_in(
acc,
[:next_transaction, Access.key(:data, %{}), Access.key(:content)],
previous_content
)
end

defp chain_content(acc), do: acc

defp chain_ownerships(
acc = %{
next_transaction: %Transaction{data: %TransactionData{ownerships: []}},
Expand Down
22 changes: 15 additions & 7 deletions test/archethic/contracts/worker_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ defmodule Archethic.Contracts.WorkerTest do
alias Archethic.TransactionChain.TransactionData.UCOLedger.Transfer
alias Archethic.TransactionChain.TransactionData.TokenLedger
alias Archethic.TransactionChain.TransactionData.TokenLedger.Transfer, as: TokenTransfer
alias Archethic.TransactionChain.Transaction.ValidationStamp
alias Archethic.TransactionChain.Transaction.ValidationStamp.LedgerOperations.UnspentOutput

alias Archethic.PubSub
Expand Down Expand Up @@ -80,7 +81,8 @@ defmodule Archethic.Contracts.WorkerTest do
previous_public_key:
transaction_seed
|> Crypto.derive_keypair(0)
|> elem(0)
|> elem(0),
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
}
|> Constants.from_transaction()

Expand Down Expand Up @@ -255,7 +257,8 @@ defmodule Archethic.Contracts.WorkerTest do
assert :ok =
Worker.execute(contract_address, %Transaction{
address: "@Bob3",
data: %TransactionData{}
data: %TransactionData{},
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
})

receive do
Expand Down Expand Up @@ -301,7 +304,8 @@ defmodule Archethic.Contracts.WorkerTest do
assert :ok =
Worker.execute(contract_address, %Transaction{
address: "@Bob3",
data: %TransactionData{content: "Mr.X"}
data: %TransactionData{content: "Mr.X"},
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
})

receive do
Expand All @@ -317,7 +321,8 @@ defmodule Archethic.Contracts.WorkerTest do
assert {:error, :invalid_condition} =
Worker.execute(contract_address, %Transaction{
address: "@Bob3",
data: %TransactionData{content: "Mr.Z"}
data: %TransactionData{content: "Mr.Z"},
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
})

refute_receive {:transaction_sent, _}
Expand Down Expand Up @@ -367,7 +372,8 @@ defmodule Archethic.Contracts.WorkerTest do
assert :ok =
Worker.execute(contract_address, %Transaction{
address: "@Bob3",
data: %TransactionData{content: "Mr.X"}
data: %TransactionData{content: "Mr.X"},
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
})

receive do
Expand Down Expand Up @@ -428,7 +434,8 @@ defmodule Archethic.Contracts.WorkerTest do
type: :oracle,
data: %TransactionData{
content: Jason.encode!(%{"uco" => %{"eur" => 0.21}})
}
},
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
}

PubSub.notify_new_transaction("@Oracle1", :oracle, DateTime.utc_now())
Expand Down Expand Up @@ -500,7 +507,8 @@ defmodule Archethic.Contracts.WorkerTest do
}
},
recipients: [contract_address]
}
},
validation_stamp: %ValidationStamp{timestamp: DateTime.utc_now()}
})

receive do
Expand Down

0 comments on commit 6eff994

Please sign in to comment.