Skip to content

Commit

Permalink
credo
Browse files Browse the repository at this point in the history
  • Loading branch information
bchamagne committed Aug 4, 2023
1 parent b972b9a commit 2566253
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 75 deletions.
39 changes: 19 additions & 20 deletions lib/archethic/mining/pending_transaction_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -768,25 +768,28 @@ defmodule Archethic.Mining.PendingTransactionValidation do
end

defp verify_token_transaction(tx = %Transaction{data: %TransactionData{content: content}}) do
case Jason.decode(content) do
{:error, _} ->
with {:ok, json_token} <- Jason.decode(content),
:ok <- verify_token_creation(tx, json_token) do
verify_token_recipients(json_token)
else
{:error, %Jason.DecodeError{}} ->
{:error, "Invalid token transaction - invalid JSON"}

{:ok, json_token} ->
cond do
ExJsonSchema.Validator.valid?(@token_creation_schema, json_token) ->
with :ok <- verify_token_recipients(json_token),
:ok <- verify_token_creation(json_token),
do: :ok
{:error, reason} ->
{:error, reason}
end
end

ExJsonSchema.Validator.valid?(@token_resupply_schema, json_token) ->
with :ok <- verify_token_recipients(json_token),
:ok <- verify_token_resupply(tx, json_token),
do: :ok
defp verify_token_creation(tx, json_token) do
cond do
ExJsonSchema.Validator.valid?(@token_creation_schema, json_token) ->
verify_token_creation(json_token)

true ->
{:error, "Invalid token transaction - neither a token creation nor a token resupply"}
end
ExJsonSchema.Validator.valid?(@token_resupply_schema, json_token) ->
verify_token_resupply(tx, json_token)

true ->
{:error, "Invalid token transaction - neither a token creation nor a token resupply"}
end
end

Expand Down Expand Up @@ -828,7 +831,7 @@ defmodule Archethic.Mining.PendingTransactionValidation do
end

defp verify_token_resupply(tx, %{"token_reference" => token_ref}) do
with {:ok, token_address} <- Base.decode16(token_ref, case: :mixed),
with token_address <- Base.decode16!(token_ref, case: :mixed),
# verify same chain
{:ok, genesis_address} <- fetch_previous_tx_genesis_address(tx),
storage_nodes <-
Expand Down Expand Up @@ -868,9 +871,6 @@ defmodule Archethic.Mining.PendingTransactionValidation do
{:error, %Jason.DecodeError{}} ->
{:error,
"Invalid token transaction - token_reference exists but does not contain a valid JSON"}

:error ->
{:error, "Invalid token transaction - token_reference is not an hexadecimal"}
end
end

Expand All @@ -883,7 +883,6 @@ defmodule Archethic.Mining.PendingTransactionValidation do
with :ok <- validate_token_recipients_amounts(recipients, json_token),
:ok <- validate_token_recipients_total(recipients, json_token),
:ok <- validate_token_recipients_addresses(recipients) do
:ok
end
end
end
Expand Down
122 changes: 72 additions & 50 deletions lib/archethic/transaction_chain/transaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ defmodule Archethic.TransactionChain.Transaction do

alias Archethic.Utils

@token_creation_schema :archethic
|> Application.app_dir("priv/json-schemas/token-core.json")
|> File.read!()
|> Jason.decode!()
|> ExJsonSchema.Schema.resolve()

@token_resupply_schema :archethic
|> Application.app_dir("priv/json-schemas/token-resupply.json")
|> File.read!()
|> Jason.decode!()
|> ExJsonSchema.Schema.resolve()

@unit_uco 100_000_000
@version 1

Expand Down Expand Up @@ -448,56 +460,9 @@ defmodule Archethic.TransactionChain.Transaction do
}
),
case type do
:token ->
case Jason.decode(content) do
{:ok, json_content = %{"recipients" => recipients}} when is_list(recipients) ->
# resupply token transactions do not have a type, but is applied only to fungible tokens
fungible? = (json_content["type"] || "fungible") == "fungible"

Enum.map(recipients, fn r = %{"to" => address_hex, "amount" => amount} ->
token_id = r["token_id"] || if fungible?, do: 0, else: 1

token_address =
case json_content["token_reference"] do
nil ->
# it's a token creation
tx_address

token_reference_hex ->
# it's a token resupply
case Base.decode16(token_reference_hex, case: :mixed) do
{:ok, token_reference} ->
token_reference

_ ->
# failure
""
end
end

case Base.decode16(address_hex, case: :mixed) do
{:ok, address} ->
if not fungible? && amount != @unit_uco do
[]
else
%TransactionMovement{
to: address,
amount: amount,
type: {:token, token_address, token_id}
}
end

_ ->
[]
end
end)

_ ->
[]
end

_ ->
[]
:token -> get_movements_from_token_transaction(tx_address, content)
:mint_reward -> get_movements_from_token_transaction(tx_address, content)
_ -> []
end
])
end
Expand Down Expand Up @@ -984,4 +949,61 @@ defmodule Archethic.TransactionChain.Transaction do
|> Enum.map(&CrossValidationStamp.cast/1)
}
end

defp get_movements_from_token_transaction(tx_address, tx_content) do
case Jason.decode(tx_content) do
{:ok, json} ->
cond do
ExJsonSchema.Validator.valid?(@token_creation_schema, json) ->
get_movements_from_token_creation(tx_address, json)

ExJsonSchema.Validator.valid?(@token_resupply_schema, json) ->
get_movements_from_token_resupply(json)

true ->
[]
end

{:error, _} ->
[]
end
end

defp get_movements_from_token_creation(tx_address, json) do
recipients = json["recipients"] || []
fungible? = json["type"] == "fungible"

Enum.map(recipients, fn r = %{"to" => address_hex, "amount" => amount} ->
token_id = r["token_id"] || if fungible?, do: 0, else: 1
address = Base.decode16!(address_hex, case: :mixed)

if not fungible? and amount != @unit_uco do
nil
else
%TransactionMovement{
to: address,
amount: amount,
type: {:token, tx_address, token_id}
}
end
end)
|> Enum.reject(&is_nil/1)
end

defp get_movements_from_token_resupply(json) do
recipients = json["recipients"] || []
token_reference = json["token_reference"]

Enum.map(recipients, fn r = %{"to" => address_hex, "amount" => amount} ->
token_id = r["token_id"] || 0
token_address = Base.decode16!(token_reference, case: :mixed)
address = Base.decode16!(address_hex, case: :mixed)

%TransactionMovement{
to: address,
amount: amount,
type: {:token, token_address, token_id}
}
end)
end
end
24 changes: 23 additions & 1 deletion priv/json-schemas/token-core.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$defs": {
"address": {
"oneOf": [
{
"type": "string",
"pattern": "^0[0-2]0[025][0-9a-fA-F]{64}$"
},
{
"type": "string",
"pattern": "^0[0-2]0[134][0-9a-fA-F]{128}$"
}
]
}
},
"type": "object",
"properties": {
"supply": {
Expand Down Expand Up @@ -44,9 +58,17 @@
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"to",
"amount"
],
"properties": {
"token_id": {
"type": "integer",
"description": "The index of the token in a collection"
},
"to": {
"type": "string",
"$ref": "#/$defs/address",
"description": "Recipient address"
},
"amount": {
Expand Down
28 changes: 25 additions & 3 deletions priv/json-schemas/token-resupply.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"$defs": {
"address": {
"oneOf": [
{
"type": "string",
"pattern": "^0[0-2]0[025][0-9a-fA-F]{64}$"
},
{
"type": "string",
"pattern": "^0[0-2]0[134][0-9a-fA-F]{128}$"
}
]
}
},
"properties": {
"supply": {
"type": "integer",
Expand All @@ -16,7 +30,7 @@
}
},
"token_reference": {
"type": "string",
"$ref": "#/$defs/address",
"description": "Address of the fungible token to resupply"
},
"recipients": {
Expand All @@ -25,10 +39,18 @@
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"to",
"amount"
],
"properties": {
"token_id": {
"type": "integer",
"description": "The index of the token in a collection"
},
"to": {
"type": "string",
"description": "Recipient address"
"$ref": "#/$defs/address",
"description": "The recipient's address"
},
"amount": {
"type": "integer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1347,7 +1347,7 @@ defmodule Archethic.Mining.PendingTransactionValidationTest do
0
)

assert {:error, "Invalid token transaction - token_reference is not an hexadecimal"} =
assert {:error, "Invalid token transaction - neither a token creation nor a token resupply"} =
PendingTransactionValidation.validate(tx)
end

Expand Down
19 changes: 19 additions & 0 deletions test/archethic/transaction_chain/transaction_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ defmodule Archethic.TransactionChain.TransactionTest do
test "should return an empty list if invalid transaction" do
token = random_address()
token_hex = token |> Base.encode16()
recipient1 = random_address()
recipient1_hex = recipient1 |> Base.encode16()

assert [] =
Transaction.get_movements(
Expand All @@ -231,6 +233,23 @@ defmodule Archethic.TransactionChain.TransactionTest do
)
)

assert [] =
Transaction.get_movements(
TransactionFactory.create_valid_transaction([],
type: :token,
content: """
{
"token_reference": "not an hexadecimal",
"supply": 100000000,
"recipients": [{
"to": "#{recipient1_hex}",
"amount": 1000
}]
}
"""
)
)

assert [] =
Transaction.get_movements(
TransactionFactory.create_valid_transaction([],
Expand Down

0 comments on commit 2566253

Please sign in to comment.