From bc56892fc5d796834742d0d95bd6409e551eae6a Mon Sep 17 00:00:00 2001 From: Edwin Steven Guayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Fri, 27 Jan 2023 09:41:02 -0500 Subject: [PATCH 1/3] YAML reader implementation (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add YAML reader implementation Co-authored-by: David Roldán --- lib/pact/command/yaml_reader.ex | 72 ++++++++++++ mix.exs | 1 + mix.lock | 2 + test/pact/command/yaml_reader_test.exs | 111 ++++++++++++++++++ .../yaml_tests_files/data_hello_world.json | 18 +++ .../support/yaml_tests_files/hello_world.pact | 38 ++++++ test/support/yaml_tests_files/with_files.yaml | 2 + .../with_files_code_error.yaml | 2 + .../with_files_data_error.yaml | 2 + .../yaml_tests_files/with_files_no_code.yaml | 1 + .../yaml_tests_files/with_files_no_data.yaml | 1 + .../yaml_tests_files/with_files_not_set.yaml | 2 + .../support/yaml_tests_files/with_values.yaml | 15 +++ .../yaml_tests_files/with_values_not_set.yaml | 2 + 14 files changed, 269 insertions(+) create mode 100644 lib/pact/command/yaml_reader.ex create mode 100644 test/pact/command/yaml_reader_test.exs create mode 100644 test/support/yaml_tests_files/data_hello_world.json create mode 100644 test/support/yaml_tests_files/hello_world.pact create mode 100644 test/support/yaml_tests_files/with_files.yaml create mode 100644 test/support/yaml_tests_files/with_files_code_error.yaml create mode 100644 test/support/yaml_tests_files/with_files_data_error.yaml create mode 100644 test/support/yaml_tests_files/with_files_no_code.yaml create mode 100644 test/support/yaml_tests_files/with_files_no_data.yaml create mode 100644 test/support/yaml_tests_files/with_files_not_set.yaml create mode 100644 test/support/yaml_tests_files/with_values.yaml create mode 100644 test/support/yaml_tests_files/with_values_not_set.yaml diff --git a/lib/pact/command/yaml_reader.ex b/lib/pact/command/yaml_reader.ex new file mode 100644 index 0000000..8331d84 --- /dev/null +++ b/lib/pact/command/yaml_reader.ex @@ -0,0 +1,72 @@ +defmodule Kadena.Pact.Command.YamlReader do + @moduledoc """ + Reads YAML file to create a command. + """ + + @type path :: String.t() + @type root_path :: String.t() + @type map_result :: map() + @type processed_map :: {:ok, map_result()} | {:error, Keyword.t() | struct()} + + @spec read(path :: path()) :: processed_map() + def read(path) do + root_path = Path.dirname(path) + + with {:ok, map_result} <- YamlElixir.read_from_file(path), + {:ok, map_result} <- process_code(map_result, root_path) do + process_data(map_result, root_path) + end + end + + @spec process_code(map_result :: map_result(), root_path :: root_path()) :: processed_map() + defp process_code(%{"codeFile" => code_file} = map_result, root_path) + when is_binary(code_file) do + case String.match?(code_file, ~r{^.*\.(pact)$}) do + true -> + map_result + |> Map.put("code", File.read!(root_path <> "/#{code_file}")) + |> Map.delete("codeFile") + |> (&{:ok, &1}).() + + false -> + {:error, [code_file: :not_a_pact_file]} + end + end + + defp process_code(%{"codeFile" => nil} = map_result, _root_path), + do: {:ok, Map.delete(map_result, "codeFile")} + + defp process_code(%{"code" => code} = map_result, _root_path) when is_binary(code), + do: {:ok, map_result} + + defp process_code(%{"code" => nil} = map_result, _root_path), + do: {:ok, Map.delete(map_result, "code")} + + defp process_code(map_result, _root_path), do: {:ok, map_result} + + @spec process_data(map_result :: map_result(), root_path :: root_path()) :: processed_map() + defp process_data(%{"dataFile" => data_file} = yaml, root_path) when is_binary(data_file) do + case String.match?(data_file, ~r{^.*\.(json)$}) do + true -> + (root_path <> "/#{data_file}") + |> File.read!() + |> Jason.decode!() + |> (&Map.put(yaml, "data", &1)).() + |> Map.delete("dataFile") + |> (&{:ok, &1}).() + + false -> + {:error, [data_file: :not_a_json_file]} + end + end + + defp process_data(%{"dataFile" => nil} = map_result, _root_path), + do: {:ok, Map.delete(map_result, "dataFile")} + + defp process_data(%{"data" => %{}} = map_result, _root_path), do: {:ok, map_result} + + defp process_data(%{"data" => nil} = map_result, _root_path), + do: {:ok, Map.delete(map_result, "data")} + + defp process_data(map_result, _root_path), do: {:ok, map_result} +end diff --git a/mix.exs b/mix.exs index 39fd6a9..f035ab0 100644 --- a/mix.exs +++ b/mix.exs @@ -43,6 +43,7 @@ defmodule Kadena.MixProject do {:blake2, "~> 1.0.4"}, {:decimal, "~> 2.0"}, {:hackney, "~> 1.18"}, + {:yaml_elixir, "~> 2.9"}, {:credo, "~> 1.6", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}, {:ex_doc, "~> 0.24", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index ebff3d9..5a75d13 100644 --- a/mix.lock +++ b/mix.lock @@ -22,4 +22,6 @@ "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, + "yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"}, } diff --git a/test/pact/command/yaml_reader_test.exs b/test/pact/command/yaml_reader_test.exs new file mode 100644 index 0000000..a00e106 --- /dev/null +++ b/test/pact/command/yaml_reader_test.exs @@ -0,0 +1,111 @@ +defmodule Kadena.Pact.Command.YamlReaderTest do + @moduledoc """ + `Pact.Command.YamlReader` functions tests. + """ + + use ExUnit.Case + + alias Kadena.Pact.Command.YamlReader + + describe "read/1" do + setup do + yaml_result = + {:ok, + %{ + "code" => + ";;\n;; \"Hello, world!\" smart contract/module\n;;\n\n(namespace \"free\")\n\n;;---------------------------------\n;;\n;; Create an 'admin-keyset' and add some key, for loading this contract!\n;;\n;; Make sure the message is signed with this added key as well.\n;;\n;; When deploying new contracts, ensure to use a *unique keyset name*\n;; and *unique module name* from any previously deployed contract\n;;\n;;\n;;---------------------------------\n\n\n;; Keysets cannot be created in code, thus we read them in\n;; from the load message data.\n(define-keyset \"free.admin-keyset\" (read-keyset \"admin-keyset\"))\n\n;; Define the module. The module name must be unique\n(module hello-world GOV\n \"A smart contract to greet the world.\"\n\n ;; Define module governance function\n (defcap GOV ()\n (enforce-keyset \"free.admin-keyset\"))\n\n (defun hello (name:string)\n \"Do the hello-world dance\"\n (format \"Hello {}!\" [name]))\n)\n\n;; and say hello!\n(hello \"world\")\n", + "data" => %{ + "admin-keyset" => %{ + "keys" => [ + "1ead010c9e035055d125a952dbb218a81fff424631f0a5213cba21d4ddeb7426", + "6ab0d95465ae7c97c38bb04e61e9a7e09b669bb8fdf528ea4b82593b5f15b582", + "8a3990fc3621cca9cc0f303712053b1369519f5b09eea3667b28335d32dcc4ff", + "dddf8ac79971a3fd9085cafa06de30ee558d105b5f231200c36e0ddaef616244", + "e18a5b967c0a195c1f9e361b01633a32f0ae8480b749d78ff15746d55395ea1a", + "e83789c5964fa7a31525564fdf43e4c018ce2787a3a0d614839e6603fb434aba", + "eeb9b43396227ef0502a69658645c03103b438d8bcdf46bd919b6af8cf51896d" + ], + "pred" => "keys-all" + }, + "free.admin-keyset" => %{"keys" => [], "pred" => "keys-all"} + } + }} + + without_code_result = + {:ok, + %{ + "data" => %{ + "admin-keyset" => %{ + "keys" => [ + "1ead010c9e035055d125a952dbb218a81fff424631f0a5213cba21d4ddeb7426", + "6ab0d95465ae7c97c38bb04e61e9a7e09b669bb8fdf528ea4b82593b5f15b582", + "8a3990fc3621cca9cc0f303712053b1369519f5b09eea3667b28335d32dcc4ff", + "dddf8ac79971a3fd9085cafa06de30ee558d105b5f231200c36e0ddaef616244", + "e18a5b967c0a195c1f9e361b01633a32f0ae8480b749d78ff15746d55395ea1a", + "e83789c5964fa7a31525564fdf43e4c018ce2787a3a0d614839e6603fb434aba", + "eeb9b43396227ef0502a69658645c03103b438d8bcdf46bd919b6af8cf51896d" + ], + "pred" => "keys-all" + }, + "free.admin-keyset" => %{"keys" => [], "pred" => "keys-all"} + } + }} + + without_data_result = + {:ok, + %{ + "code" => + ";;\n;; \"Hello, world!\" smart contract/module\n;;\n\n(namespace \"free\")\n\n;;---------------------------------\n;;\n;; Create an 'admin-keyset' and add some key, for loading this contract!\n;;\n;; Make sure the message is signed with this added key as well.\n;;\n;; When deploying new contracts, ensure to use a *unique keyset name*\n;; and *unique module name* from any previously deployed contract\n;;\n;;\n;;---------------------------------\n\n\n;; Keysets cannot be created in code, thus we read them in\n;; from the load message data.\n(define-keyset \"free.admin-keyset\" (read-keyset \"admin-keyset\"))\n\n;; Define the module. The module name must be unique\n(module hello-world GOV\n \"A smart contract to greet the world.\"\n\n ;; Define module governance function\n (defcap GOV ()\n (enforce-keyset \"free.admin-keyset\"))\n\n (defun hello (name:string)\n \"Do the hello-world dance\"\n (format \"Hello {}!\" [name]))\n)\n\n;; and say hello!\n(hello \"world\")\n" + }} + + %{ + path: "test/support/yaml_tests_files/", + yaml_result: yaml_result, + without_code_result: without_code_result, + without_data_result: without_data_result + } + end + + test "when yaml file has code and data values", %{path: path, yaml_result: yaml_result} do + ^yaml_result = YamlReader.read(path <> "with_values.yaml") + end + + test "when yaml file has code and data files", %{path: path, yaml_result: yaml_result} do + ^yaml_result = YamlReader.read(path <> "with_files.yaml") + end + + test "without code nor code_file", %{path: path, without_code_result: without_code_result} do + ^without_code_result = YamlReader.read(path <> "with_files_no_code.yaml") + end + + test "without data nor data_file", %{path: path, without_data_result: without_data_result} do + ^without_data_result = YamlReader.read(path <> "with_files_no_data.yaml") + end + + test "without code_file and data_file set", %{path: path} do + {:ok, %{}} = YamlReader.read(path <> "with_files_not_set.yaml") + end + + test "without code and data set", %{path: path} do + {:ok, %{}} = YamlReader.read(path <> "with_values_not_set.yaml") + end + + test "error with an invalid code_file", %{path: path} do + {:error, [code_file: :not_a_pact_file]} = + YamlReader.read(path <> "with_files_code_error.yaml") + end + + test "error with an invalid data_file", %{path: path} do + {:error, [data_file: :not_a_json_file]} = + YamlReader.read(path <> "with_files_data_error.yaml") + end + + test "error with a non existing yaml", %{path: path} do + {:error, + %YamlElixir.FileNotFoundError{ + message: + "Failed to open file \"test/support/yaml_tests_files/non_existent.yaml\": no such file or directory" + }} = YamlReader.read(path <> "non_existent.yaml") + end + end +end diff --git a/test/support/yaml_tests_files/data_hello_world.json b/test/support/yaml_tests_files/data_hello_world.json new file mode 100644 index 0000000..3ab5b6b --- /dev/null +++ b/test/support/yaml_tests_files/data_hello_world.json @@ -0,0 +1,18 @@ +{ + "admin-keyset": { + "keys": [ + "1ead010c9e035055d125a952dbb218a81fff424631f0a5213cba21d4ddeb7426", + "6ab0d95465ae7c97c38bb04e61e9a7e09b669bb8fdf528ea4b82593b5f15b582", + "8a3990fc3621cca9cc0f303712053b1369519f5b09eea3667b28335d32dcc4ff", + "dddf8ac79971a3fd9085cafa06de30ee558d105b5f231200c36e0ddaef616244", + "e18a5b967c0a195c1f9e361b01633a32f0ae8480b749d78ff15746d55395ea1a", + "e83789c5964fa7a31525564fdf43e4c018ce2787a3a0d614839e6603fb434aba", + "eeb9b43396227ef0502a69658645c03103b438d8bcdf46bd919b6af8cf51896d" + ], + "pred": "keys-all" + }, + "free.admin-keyset": { + "keys": [], + "pred": "keys-all" + } +} diff --git a/test/support/yaml_tests_files/hello_world.pact b/test/support/yaml_tests_files/hello_world.pact new file mode 100644 index 0000000..e530bdb --- /dev/null +++ b/test/support/yaml_tests_files/hello_world.pact @@ -0,0 +1,38 @@ +;; +;; "Hello, world!" smart contract/module +;; + +(namespace "free") + +;;--------------------------------- +;; +;; Create an 'admin-keyset' and add some key, for loading this contract! +;; +;; Make sure the message is signed with this added key as well. +;; +;; When deploying new contracts, ensure to use a *unique keyset name* +;; and *unique module name* from any previously deployed contract +;; +;; +;;--------------------------------- + + +;; Keysets cannot be created in code, thus we read them in +;; from the load message data. +(define-keyset "free.admin-keyset" (read-keyset "admin-keyset")) + +;; Define the module. The module name must be unique +(module hello-world GOV + "A smart contract to greet the world." + + ;; Define module governance function + (defcap GOV () + (enforce-keyset "free.admin-keyset")) + + (defun hello (name:string) + "Do the hello-world dance" + (format "Hello {}!" [name])) +) + +;; and say hello! +(hello "world") diff --git a/test/support/yaml_tests_files/with_files.yaml b/test/support/yaml_tests_files/with_files.yaml new file mode 100644 index 0000000..299f566 --- /dev/null +++ b/test/support/yaml_tests_files/with_files.yaml @@ -0,0 +1,2 @@ +codeFile: hello_world.pact +dataFile: data_hello_world.json diff --git a/test/support/yaml_tests_files/with_files_code_error.yaml b/test/support/yaml_tests_files/with_files_code_error.yaml new file mode 100644 index 0000000..b9e9ea2 --- /dev/null +++ b/test/support/yaml_tests_files/with_files_code_error.yaml @@ -0,0 +1,2 @@ +codeFile: hello_world.unknown +dataFile: data_hello_world.json diff --git a/test/support/yaml_tests_files/with_files_data_error.yaml b/test/support/yaml_tests_files/with_files_data_error.yaml new file mode 100644 index 0000000..72431ea --- /dev/null +++ b/test/support/yaml_tests_files/with_files_data_error.yaml @@ -0,0 +1,2 @@ +codeFile: hello_world.pact +dataFile: data_hello_world.unknown diff --git a/test/support/yaml_tests_files/with_files_no_code.yaml b/test/support/yaml_tests_files/with_files_no_code.yaml new file mode 100644 index 0000000..1bd494a --- /dev/null +++ b/test/support/yaml_tests_files/with_files_no_code.yaml @@ -0,0 +1 @@ +dataFile: data_hello_world.json diff --git a/test/support/yaml_tests_files/with_files_no_data.yaml b/test/support/yaml_tests_files/with_files_no_data.yaml new file mode 100644 index 0000000..47893ae --- /dev/null +++ b/test/support/yaml_tests_files/with_files_no_data.yaml @@ -0,0 +1 @@ +codeFile: hello_world.pact diff --git a/test/support/yaml_tests_files/with_files_not_set.yaml b/test/support/yaml_tests_files/with_files_not_set.yaml new file mode 100644 index 0000000..3e4c4d2 --- /dev/null +++ b/test/support/yaml_tests_files/with_files_not_set.yaml @@ -0,0 +1,2 @@ +codeFile: +dataFile: diff --git a/test/support/yaml_tests_files/with_values.yaml b/test/support/yaml_tests_files/with_values.yaml new file mode 100644 index 0000000..9dd5793 --- /dev/null +++ b/test/support/yaml_tests_files/with_values.yaml @@ -0,0 +1,15 @@ +code: ";;\n;; \"Hello, world!\" smart contract/module\n;;\n\n(namespace \"free\")\n\n;;---------------------------------\n;;\n;; Create an 'admin-keyset' and add some key, for loading this contract!\n;;\n;; Make sure the message is signed with this added key as well.\n;;\n;; When deploying new contracts, ensure to use a *unique keyset name*\n;; and *unique module name* from any previously deployed contract\n;;\n;;\n;;---------------------------------\n\n\n;; Keysets cannot be created in code, thus we read them in\n;; from the load message data.\n(define-keyset \"free.admin-keyset\" (read-keyset \"admin-keyset\"))\n\n;; Define the module. The module name must be unique\n(module hello-world GOV\n \"A smart contract to greet the world.\"\n\n ;; Define module governance function\n (defcap GOV ()\n (enforce-keyset \"free.admin-keyset\"))\n\n (defun hello (name:string)\n \"Do the hello-world dance\"\n (format \"Hello {}!\" [name]))\n)\n\n;; and say hello!\n(hello \"world\")\n" +data: + admin-keyset: + keys: + - 1ead010c9e035055d125a952dbb218a81fff424631f0a5213cba21d4ddeb7426 + - 6ab0d95465ae7c97c38bb04e61e9a7e09b669bb8fdf528ea4b82593b5f15b582 + - 8a3990fc3621cca9cc0f303712053b1369519f5b09eea3667b28335d32dcc4ff + - dddf8ac79971a3fd9085cafa06de30ee558d105b5f231200c36e0ddaef616244 + - e18a5b967c0a195c1f9e361b01633a32f0ae8480b749d78ff15746d55395ea1a + - e83789c5964fa7a31525564fdf43e4c018ce2787a3a0d614839e6603fb434aba + - eeb9b43396227ef0502a69658645c03103b438d8bcdf46bd919b6af8cf51896d + pred: keys-all + free.admin-keyset: + keys: [] + pred: keys-all diff --git a/test/support/yaml_tests_files/with_values_not_set.yaml b/test/support/yaml_tests_files/with_values_not_set.yaml new file mode 100644 index 0000000..cbc1bb0 --- /dev/null +++ b/test/support/yaml_tests_files/with_values_not_set.yaml @@ -0,0 +1,2 @@ +code: +data: From 49036bb557767722de6cd99a5d4fa798f713773b Mon Sep 17 00:00:00 2001 From: Edwin Steven Guayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Tue, 31 Jan 2023 09:43:00 -0500 Subject: [PATCH 2/3] YAML execution command implementation (#270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add YAML execution command implementation Co-authored-by: David Roldán --- README.md | 97 ++++++++++++++++++ lib/pact/command/command.ex | 2 +- lib/pact/command/cont_command.ex | 3 + lib/pact/command/exec_command.ex | 70 ++++++++++++- lib/pact/command/yaml_reader.ex | 2 +- lib/types/cap.ex | 4 +- lib/types/keypair.ex | 22 ++++- lib/types/meta_data.ex | 27 ++++- lib/types/signer.ex | 24 ++++- test/chainweb/pact/command_payload_test.exs | 20 ++-- test/chainweb/pact/send_test.exs | 25 ++++- test/pact/command/cont_command_test.exs | 27 ++++- test/pact/command/exec_command_test.exs | 99 ++++++++++++++++++- test/support/fixtures/chainweb/send_2.json | 3 + .../yaml_tests_files/for_test_exec.yaml | 24 +++++ .../yaml_tests_files/for_test_exec_2.yaml | 6 ++ .../yaml_tests_files/for_test_exec_3.yaml | 7 ++ .../yaml_tests_files/for_test_exec_4.yaml | 24 +++++ .../yaml_tests_files/for_test_exec_5.yaml | 14 +++ .../yaml_tests_files/for_test_exec_6.yaml | 24 +++++ .../yaml_tests_files/for_test_exec_7.yaml | 17 ++++ .../yaml_tests_files/for_test_exec_8.yaml | 24 +++++ .../yaml_tests_files/for_test_send.yaml | 13 +++ test/types/cap_test.exs | 2 +- test/types/keypair_test.exs | 33 ++++++- test/types/meta_data_test.exs | 23 ++++- test/types/signer_test.exs | 46 ++++++++- 27 files changed, 652 insertions(+), 30 deletions(-) create mode 100644 test/support/fixtures/chainweb/send_2.json create mode 100644 test/support/yaml_tests_files/for_test_exec.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_2.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_3.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_4.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_5.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_6.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_7.yaml create mode 100644 test/support/yaml_tests_files/for_test_exec_8.yaml create mode 100644 test/support/yaml_tests_files/for_test_send.yaml diff --git a/README.md b/README.md index 07a4e51..30da3e9 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,10 @@ Pact.ContCommand.new() |> Pact.ContCommand.set_pact_tx_hash(pact_tx_hash) ### Building an Execution Command +There are two ways to create an ExecCommand. + +#### Using [attributes](#attributes) structures + ```elixir alias Kadena.Cryptography alias Kadena.Pact @@ -346,6 +350,99 @@ env_data = %{accounts_admin_keyset: [keypair.pub_key]} }} ``` +#### With a `YAML` file + +YAML struct: + +- `networkId`: [NetworkID](#networkid) value. +- `code`: there are two ways to set the code from the `YAML` file: + - `code`: [Code](#code) value. + - `codeFile`: The name of a `pact` file in the same directory as the `YAML` file. +- `data`: there are two ways to set the data from the `YAML` file: + - `data`: [EnvData](#envdata) value. + - `dataFile`: The name of a `json` file in the same directory as the `YAML` file. +- `nonce`: [Nonce](#nonce) value. +- `publicMeta`: [Metadata](#metadata) value. +- `keyPairs`: [KeyPairs](#keypairs) values. +- `signers`: [Signers](#signers) values. + +The scheme below shows how to set the different values of an `ExecCommand` + +```YAML +networkId: +code/codeFile: +data/dataFile: +nonce: +publicMeta: + creationTime: + chainId: + gasLimit: + gasPrice: + ttl: + sender: +keyPairs: + - public: + secret: +signers: + - publicKey: + scheme: + addr: + capsList: + - name: + args: + - +``` +**Example** + +YAML file: +```YAML +networkId: :testnet04 +code: "(+ 1 2)" +data: + accounts_admin_keyset: + - 6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7 +nonce: 2023-01-01 00:00:00.000000 UTC +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 2500 + gasPrice: 0.01 + ttl: 28800 + sender: k:6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7 +keyPairs: + - public: 6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7 + secret: 99f7e1e8f2f334ae8374aa28bebdb997271a0e0a5e92c80be9609684a3d6f0d4 + capsList: + name: coin.GAS + args: + - 6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7 + +``` + +```elixir +alias Kadena.Pact.ExecCommand + +"~/your_file_path" +|> ExecCommand.from_yaml() +|> ExecCommand.build() + +{:ok, + %Kadena.Types.Command{ + cmd: + "{\"meta\":{\"chainId\":\"0\",\"creationTime\":1667249173,\"gasLimit\":2500,\"gasPrice\":0.01,\"sender\":\"k:6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\",\"ttl\":28800},\"networkId\":\"testnet04\",\"nonce\":\"2023-01-01 00:00:00.000000 UTC\",\"payload\":{\"exec\":{\"code\":\"(+ 1 2)\",\"data\":{\"accounts_admin_keyset\":[\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\"]}}},\"signers\":[{\"addr\":null,\"clist\":[{\"args\":[\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\",\"scheme\":\"ED25519\"}]}", + hash: %Kadena.Types.PactTransactionHash{ + hash: "ZOsqP9Wkfj5NnY9WS_XMnO9KfYv0GvK_8QMPTX6BfaA" + }, + sigs: [ + %Kadena.Types.Signature{ + sig: + "5b0635c2376949103d8ce8243a1fd34a1a8964900d69eebb1ff4d38ffde437a317f887f864fa9c1b27a97e8d9e57eef8dd58b054edf30b4e6fe89d0208290f02" + } + ] + }} + +``` + ### Building a Continuation Command ```elixir diff --git a/lib/pact/command/command.ex b/lib/pact/command/command.ex index cd3dbdd..bcdc5f5 100644 --- a/lib/pact/command/command.ex +++ b/lib/pact/command/command.ex @@ -13,7 +13,6 @@ defmodule Kadena.Pact.Command do @type exec_request :: ExecCommand.t() @type error :: {:error, Keyword.t()} @type cmd_request :: cont_request() | exec_request() - @type string_value :: String.t() @type meta_data :: MetaData.t() @type keypair :: KeyPair.t() @@ -23,6 +22,7 @@ defmodule Kadena.Pact.Command do @type hash :: String.t() @callback new() :: cmd_request() + @callback from_yaml(path :: string_value()) :: cmd_request() @callback set_network(cmd :: cmd_request(), network :: atom()) :: cmd_request() @callback set_data(cmd :: cmd_request(), data :: map()) :: cmd_request() @callback set_nonce(cmd :: cmd_request(), nonce :: string_value()) :: cmd_request() diff --git a/lib/pact/command/cont_command.ex b/lib/pact/command/cont_command.ex index bdc974f..90593d6 100644 --- a/lib/pact/command/cont_command.ex +++ b/lib/pact/command/cont_command.ex @@ -102,6 +102,9 @@ defmodule Kadena.Pact.ContCommand do def new(_opts), do: %__MODULE__{} + @impl true + def from_yaml(_path), do: new() + @impl true def set_network(%__MODULE__{} = cmd_request, network) do case NetworkID.new(network) do diff --git a/lib/pact/command/exec_command.ex b/lib/pact/command/exec_command.ex index ec6346f..e60c45d 100644 --- a/lib/pact/command/exec_command.ex +++ b/lib/pact/command/exec_command.ex @@ -6,7 +6,7 @@ defmodule Kadena.Pact.ExecCommand do alias Kadena.Chainweb.Pact.CommandPayload alias Kadena.Cryptography.{Sign, Utils} - alias Kadena.Pact.Command.Hash + alias Kadena.Pact.Command.{Hash, YamlReader} alias Kadena.Types.{ Command, @@ -87,6 +87,30 @@ defmodule Kadena.Pact.ExecCommand do def new(_opts), do: %__MODULE__{} + @impl true + def from_yaml(path) when is_binary(path) do + with {:ok, map_result} <- YamlReader.read(path) do + network_id = Map.get(map_result, "networkId") + code = Map.get(map_result, "code", "") + data = Map.get(map_result, "data") + nonce = Map.get(map_result, "nonce", "") + meta_data = Map.get(map_result, "publicMeta", MetaData.new()) + keypairs = Map.get(map_result, "keyPairs", []) + signers = Map.get(map_result, "signers", []) + + %__MODULE__{} + |> process_metadata(meta_data) + |> process_keypairs(keypairs) + |> process_signers(signers) + |> set_network(network_id) + |> set_data(data) + |> set_code(code) + |> set_nonce(nonce) + end + end + + def from_yaml(_path), do: {:error, [path: :invalid]} + @impl true def set_network(%__MODULE__{} = cmd_request, network) do case NetworkID.new(network) do @@ -267,4 +291,48 @@ defmodule Kadena.Pact.ExecCommand do defp build_signatures([%SignCommand{sig: sig} | rest], result), do: build_signatures(rest, result ++ [Signature.new(sig)]) + + defp process_metadata(%__MODULE__{} = cmd_request, %MetaData{} = metadata), + do: set_metadata(cmd_request, metadata) + + defp process_metadata(%__MODULE__{} = cmd_request, %{} = metadata) do + case MetaData.new(metadata) do + %MetaData{} = result -> %{cmd_request | meta_data: result} + {:error, reason} -> {:error, [meta_data: :invalid] ++ reason} + end + end + + defp process_metadata(%__MODULE__{}, _metadata), do: {:error, [metadata: :invalid]} + + defp process_keypairs(%__MODULE__{} = cmd_request, [%{} = keypair_data | rest]) do + case KeyPair.new(keypair_data) do + %KeyPair{} = result -> + cmd_request + |> add_keypair(result) + |> process_keypairs(rest) + + {:error, reason} -> + {:error, [keypair: :invalid] ++ reason} + end + end + + defp process_keypairs(%__MODULE__{} = cmd_request, []), do: cmd_request + defp process_keypairs(%__MODULE__{}, _keypair), do: {:error, [keypair: :invalid]} + defp process_keypairs({:error, reason}, _keypairs), do: {:error, reason} + + defp process_signers(%__MODULE__{} = cmd_request, [%{} = signers_data | rest]) do + case Signer.new(signers_data) do + %Signer{} = result -> + cmd_request + |> add_signer(result) + |> process_signers(rest) + + {:error, reason} -> + {:error, [signers: :invalid] ++ reason} + end + end + + defp process_signers(%__MODULE__{} = cmd_request, []), do: cmd_request + defp process_signers({:error, reason}, _signers), do: {:error, reason} + defp process_signers(%__MODULE__{}, _signers), do: {:error, [signers: :invalid]} end diff --git a/lib/pact/command/yaml_reader.ex b/lib/pact/command/yaml_reader.ex index 8331d84..33243f3 100644 --- a/lib/pact/command/yaml_reader.ex +++ b/lib/pact/command/yaml_reader.ex @@ -12,7 +12,7 @@ defmodule Kadena.Pact.Command.YamlReader do def read(path) do root_path = Path.dirname(path) - with {:ok, map_result} <- YamlElixir.read_from_file(path), + with {:ok, map_result} <- YamlElixir.read_from_file(path, atoms: true), {:ok, map_result} <- process_code(map_result, root_path) do process_data(map_result, root_path) end diff --git a/lib/types/cap.ex b/lib/types/cap.ex index daec4dc..40b1a86 100644 --- a/lib/types/cap.ex +++ b/lib/types/cap.ex @@ -27,8 +27,8 @@ defmodule Kadena.Types.Cap do end def new(args) when is_map(args) do - name = Map.get(args, :name) - args = Map.get(args, :args) + name = Map.get(args, "name") + args = Map.get(args, "args") with {:ok, name} <- validate_name(name), {:ok, args} <- validate_args(args) do diff --git a/lib/types/keypair.ex b/lib/types/keypair.ex index aee18bd..fb83671 100644 --- a/lib/types/keypair.ex +++ b/lib/types/keypair.ex @@ -31,7 +31,19 @@ defmodule Kadena.Types.KeyPair do end end - def new(_args), do: {:error, [args: :not_a_list]} + def new(args) when is_map(args) do + pub_key_arg = Map.get(args, "public") + secret_key_arg = Map.get(args, "secret") + clist_arg = Map.get(args, "capsList") + + with {:ok, pub_key} <- validate_key({:pub_key, pub_key_arg}), + {:ok, secret_key} <- validate_key({:secret_key, secret_key_arg}), + {:ok, clist} <- create_clist({:clist, clist_arg}) do + %__MODULE__{pub_key: pub_key, secret_key: secret_key, clist: clist} + end + end + + def new(_args), do: {:error, [args: :invalid]} @spec add_caps(keypair :: t(), caps :: clist()) :: validated_keypair() def add_caps(%__MODULE__{} = keypair, caps) do @@ -49,4 +61,12 @@ defmodule Kadena.Types.KeyPair do defp validate_caps_list({_arg, nil}), do: {:ok, nil} defp validate_caps_list({_arg, [%Cap{} | _tail] = clist}), do: {:ok, clist} defp validate_caps_list({arg, _clist}), do: {:error, [{arg, :invalid}]} + + defp create_clist({arg, caps}) when is_list(caps) do + caps + |> Enum.map(fn cap -> Cap.new(cap) end) + |> (&validate_caps_list({arg, &1})).() + end + + defp create_clist({_arg, nil}), do: {:ok, nil} end diff --git a/lib/types/meta_data.ex b/lib/types/meta_data.ex index f6a37df..10ed972 100644 --- a/lib/types/meta_data.ex +++ b/lib/types/meta_data.ex @@ -56,7 +56,32 @@ defmodule Kadena.Types.MetaData do end end - def new(_args), do: {:error, [args: :not_a_list]} + def new(args) when is_map(args) do + creation_time = Map.get(args, "creationTime", 0) + ttl = Map.get(args, "ttl", 0) + gas_limit = Map.get(args, "gasLimit", 0) + gas_price = Map.get(args, "gasPrice", 0) + sender = Map.get(args, "sender", "") + chain_id = Map.get(args, "chainId", "0") + + with {:ok, creation_time} <- validate_creation_time(creation_time), + {:ok, ttl} <- validate_number(:ttl, ttl), + {:ok, gas_limit} <- validate_number(:gas_limit, gas_limit), + {:ok, gas_price} <- validate_number(:gas_price, gas_price), + {:ok, sender} <- validate_sender(sender), + {:ok, chain_id} <- validate_chain_id(chain_id) do + %__MODULE__{ + creation_time: creation_time, + ttl: ttl, + gas_limit: gas_limit, + gas_price: gas_price, + sender: sender, + chain_id: chain_id + } + end + end + + def new(_args), do: {:error, [args: :invalid]} @spec validate_creation_time(creation_time :: creation_time()) :: validation() defp validate_creation_time(creation_time) when is_number(creation_time), diff --git a/lib/types/signer.ex b/lib/types/signer.ex index 87faccd..ee38d13 100644 --- a/lib/types/signer.ex +++ b/lib/types/signer.ex @@ -25,7 +25,7 @@ defmodule Kadena.Types.Signer do defstruct [:pub_key, :scheme, :addr, :clist] @impl true - def new(args) do + def new(args) when is_list(args) do pub_key = Keyword.get(args, :pub_key) scheme = Keyword.get(args, :scheme) addr = Keyword.get(args, :addr) @@ -39,6 +39,20 @@ defmodule Kadena.Types.Signer do end end + def new(args) when is_map(args) do + pub_key = Map.get(args, "publicKey") + scheme = Map.get(args, "scheme") + addr = Map.get(args, "addr") + clist = Map.get(args, "capsList") + + with {:ok, pub_key} <- validate_pub_key(pub_key), + {:ok, scheme} <- validate_scheme(scheme), + {:ok, addr} <- validate_addr(addr), + {:ok, clist} <- create_clist(clist) do + %__MODULE__{pub_key: pub_key, scheme: scheme, addr: addr, clist: clist} + end + end + @spec validate_pub_key(pub_key :: str()) :: validation() defp validate_pub_key(pub_key) when is_binary(pub_key), do: {:ok, Base16String.new(pub_key)} defp validate_pub_key(_pub_key), do: {:error, [pub_key: :invalid]} @@ -57,4 +71,12 @@ defmodule Kadena.Types.Signer do defp validate_clist(nil), do: {:ok, nil} defp validate_clist([%Cap{} | _tail] = clist), do: {:ok, clist} defp validate_clist(_clist), do: {:error, [clist: :not_a_caps_list]} + + defp create_clist(caps) when is_list(caps) do + caps + |> Enum.map(fn cap -> Cap.new(cap) end) + |> validate_clist() + end + + defp create_clist(nil), do: {:ok, nil} end diff --git a/test/chainweb/pact/command_payload_test.exs b/test/chainweb/pact/command_payload_test.exs index 5358de9..543db5c 100644 --- a/test/chainweb/pact/command_payload_test.exs +++ b/test/chainweb/pact/command_payload_test.exs @@ -28,7 +28,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do rollback = true # Signer args - cap = Cap.new(%{name: "gas", args: ["coin.GAS", 0.02]}) + cap = Cap.new(name: "gas", args: ["coin.GAS", 0.02]) base16string = "64617373646164617364617364616473616461736461736464" signer_scheme = :ed25519 @@ -319,7 +319,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do signer_scheme: signer_scheme, exec_payload: exec_payload } do - cap = Cap.new(%{name: "coin.GAS", args: [pub_key]}) + cap = Cap.new(name: "coin.GAS", args: [pub_key]) signer_value = [ pub_key: pub_key, @@ -362,7 +362,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do signer_scheme: signer_scheme, exec_payload: exec_payload } do - cap = Cap.new(%{name: "coin.GAS", args: [9_007_199_254_740_992]}) + cap = Cap.new(name: "coin.GAS", args: [9_007_199_254_740_992]) signer_value = [ pub_key: pub_key, @@ -405,7 +405,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do signer_scheme: signer_scheme, exec_payload: exec_payload } do - cap = Cap.new(%{name: "coin.GAS", args: ["9007199254740992.553"]}) + cap = Cap.new(name: "coin.GAS", args: ["9007199254740992.553"]) signer_value = [ pub_key: pub_key, @@ -448,7 +448,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do signer_scheme: signer_scheme, exec_payload: exec_payload } do - cap = Cap.new(%{name: "coin.GAS", args: [["9007199254740992.553"]]}) + cap = Cap.new(name: "coin.GAS", args: [["9007199254740992.553"]]) signer_value = [ pub_key: pub_key, @@ -491,7 +491,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do signer_scheme: signer_scheme, exec_payload: exec_payload } do - cap = Cap.new(%{name: "coin.GAS", args: [pub_key]}) + cap = Cap.new(name: "coin.GAS", args: [pub_key]) signer_value = [ pub_key: pub_key, @@ -533,7 +533,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do pub_key: pub_key, exec_payload: exec_payload } do - cap = Cap.new(%{name: "coin.GAS", args: [pub_key]}) + cap = Cap.new(name: "coin.GAS", args: [pub_key]) signer_value = [ pub_key: pub_key, @@ -616,7 +616,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do pub_key: pub_key, signer_scheme: signer_scheme } do - cap = Cap.new(%{name: "coin.GAS", args: [pub_key]}) + cap = Cap.new(name: "coin.GAS", args: [pub_key]) signer_value = [ pub_key: pub_key, @@ -658,7 +658,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do pub_key: pub_key, signer_scheme: signer_scheme } do - cap = Cap.new(%{name: "coin.GAS", args: [["9007199254740992.553"]]}) + cap = Cap.new(name: "coin.GAS", args: [["9007199254740992.553"]]) signer_value = [ pub_key: pub_key, @@ -709,7 +709,7 @@ defmodule Kadena.Chainweb.Pact.CommandPayloadTest do pub_key: pub_key, signer_scheme: signer_scheme } do - cap = Cap.new(%{name: "coin.GAS", args: [["9007199254740992.553"]]}) + cap = Cap.new(name: "coin.GAS", args: [["9007199254740992.553"]]) signer_value = [ pub_key: pub_key, diff --git a/test/chainweb/pact/send_test.exs b/test/chainweb/pact/send_test.exs index c1ad2a2..a791294 100644 --- a/test/chainweb/pact/send_test.exs +++ b/test/chainweb/pact/send_test.exs @@ -4,6 +4,17 @@ defmodule Kadena.Chainweb.Client.CannedSendRequests do alias Kadena.Chainweb.Error alias Kadena.Test.Fixtures.Chainweb + def request( + :post, + "https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/1/pact/api/v1/send", + _headers, + "{\"cmds\":[{\"cmd\":\"{\\\"meta\\\":{\\\"chainId\\\":\\\"1\\\",\\\"creationTime\\\":1675093790,\\\"gasLimit\\\":1000,\\\"gasPrice\\\":1.0e-6,\\\"sender\\\":\\\"k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7\\\",\\\"ttl\\\":28800},\\\"networkId\\\":\\\"testnet04\\\",\\\"nonce\\\":\\\"2022-01-01 00:00:00.000000 UTC\\\",\\\"payload\\\":{\\\"exec\\\":{\\\"code\\\":\\\"(+ 1 10)\\\",\\\"data\\\":null}},\\\"signers\\\":[{\\\"addr\\\":null,\\\"clist\\\":[],\\\"pubKey\\\":\\\"d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7\\\",\\\"scheme\\\":\\\"ED25519\\\"}]}\",\"hash\":\"64rRUBVnKEJOw0-zjqqAoFz5KRPHN95uPRt9hNi44jM\",\"sigs\":[{\"sig\":\"9d034184bac92b896d22a3ba6473955e5080e8341d3016cf43bc9c62de69a8796dd4efc1ab23cd9db2521cca9bd5d271c24a0981daa749d11e9ab18385cdd907\"}]}]}", + _options + ) do + response = Chainweb.fixture("send_2") + {:ok, response} + end + def request( :post, "https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/1/pact/api/v1/send", @@ -96,8 +107,13 @@ defmodule Kadena.Chainweb.Pact.SendTest do |> ExecCommand.add_keypair(keypair) |> ExecCommand.build() + {:ok, cmd_from_yaml} = + ExecCommand.from_yaml("test/support/yaml_tests_files/for_test_send.yaml") + |> ExecCommand.build() + %{ - cmds: [cmd1, cmd2] + cmds: [cmd1, cmd2], + cmd_yaml: [cmd_from_yaml] } end @@ -111,6 +127,13 @@ defmodule Kadena.Chainweb.Pact.SendTest do }} = Pact.send(cmds, network_id: :testnet04, chain_id: 1) end + test "process with a ExecCommand from a YAML file", %{cmd_yaml: cmd_yaml} do + {:ok, + %SendResponse{ + request_keys: ["64rRUBVnKEJOw0-zjqqAoFz5KRPHN95uPRt9hNi44jM"] + }} = Pact.send(cmd_yaml, network_id: :testnet04, chain_id: 1) + end + test "process/2 conflict with chain_id", %{cmds: cmds} do {:error, %Error{ diff --git a/test/pact/command/cont_command_test.exs b/test/pact/command/cont_command_test.exs index 6d6396b..1b2ddd8 100644 --- a/test/pact/command/cont_command_test.exs +++ b/test/pact/command/cont_command_test.exs @@ -18,12 +18,37 @@ defmodule Kadena.Pact.ContCommandTest do Signer } + describe "create ContCommand with YAMl file" do + setup do + path = "" + + %{ + path: path + } + end + + test "with a valid YAML file", %{path: path} do + %Kadena.Pact.ContCommand{ + data: nil, + keypairs: [], + meta_data: nil, + network_id: nil, + nonce: nil, + pact_tx_hash: nil, + proof: nil, + rollback: nil, + signers: [], + step: nil + } = ContCommand.from_yaml(path) + end + end + describe "create ContCommand" do setup do secret_key = "99f7e1e8f2f334ae8374aa28bebdb997271a0e0a5e92c80be9609684a3d6f0d4" {:ok, %KeyPair{pub_key: pub_key}} = CryptographyKeyPair.from_secret_key(secret_key) - cap = Cap.new(%{name: "coin.GAS", args: [pub_key]}) + cap = Cap.new(name: "coin.GAS", args: [pub_key]) clist = [cap] keypair_data = [pub_key: pub_key, secret_key: secret_key, clist: clist] diff --git a/test/pact/command/exec_command_test.exs b/test/pact/command/exec_command_test.exs index 9afe7aa..a4ed736 100644 --- a/test/pact/command/exec_command_test.exs +++ b/test/pact/command/exec_command_test.exs @@ -18,11 +18,108 @@ defmodule Kadena.Pact.ExecCommandTest do Signer } + describe "create ExecCommand with YAMl file" do + setup do + path = "test/support/yaml_tests_files/for_test_exec.yaml" + path2 = "test/support/yaml_tests_files/for_test_exec_2.yaml" + path3 = "test/support/yaml_tests_files/for_test_exec_3.yaml" + path4 = "test/support/yaml_tests_files/for_test_exec_4.yaml" + path5 = "test/support/yaml_tests_files/for_test_exec_5.yaml" + path6 = "test/support/yaml_tests_files/for_test_exec_6.yaml" + path7 = "test/support/yaml_tests_files/for_test_exec_7.yaml" + path8 = "test/support/yaml_tests_files/for_test_exec_8.yaml" + bad_path = "test/support/yaml_tests_files/no_existent.yaml" + + %{ + path: path, + path2: path2, + path3: path3, + path4: path4, + path5: path5, + path6: path6, + path7: path7, + path8: path8, + bad_path: bad_path + } + end + + test "with a valid YAML file", %{path: path} do + {:ok, + %Kadena.Types.Command{ + cmd: + "{\"meta\":{\"chainId\":\"0\",\"creationTime\":1667249173,\"gasLimit\":1000,\"gasPrice\":1.0e-6,\"sender\":\"k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94\",\"ttl\":28800},\"networkId\":\"testnet04\",\"nonce\":\"2023-06-13 17:45:18.211131 UTC\",\"payload\":{\"exec\":{\"code\":\"(+ 5 6)\",\"data\":{\"accounts-admin-keyset\":[\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\"]}}},\"signers\":[{\"addr\":null,\"clist\":[],\"pubKey\":\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\",\"scheme\":\"ED25519\"},{\"addr\":\"cc30ae980161eba5da95a0d27dbdef29f185a23406942059c16cb120f6dc9dea\",\"clist\":[{\"args\":[\"8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"cc30ae980161eba5da95a0d27dbdef29f185a23406942059c16cb120f6dc9dea\",\"scheme\":\"ED25519\"}]}", + hash: %Kadena.Types.PactTransactionHash{ + hash: "S7nmnln18fwy7_sASguWoJXw3_2svm6TWmSMQ3alCv8" + }, + sigs: [ + %Kadena.Types.Signature{ + sig: + "d0435fba35fcb57f04810d932d942de81001ba16992977bbf14761a7f5c676a2f2b91a2ab6c04b9bd509ec3392668485ae1b53ea1f1882843c12dea777636d02" + } + ] + }} = + path + |> ExecCommand.from_yaml() + |> ExecCommand.build() + end + + test "without metadata, signers and keypairs", %{path2: path2} do + {:ok, + %Kadena.Types.Command{ + cmd: + "{\"meta\":{\"chainId\":\"0\",\"creationTime\":0,\"gasLimit\":0,\"gasPrice\":0,\"sender\":\"\",\"ttl\":0},\"networkId\":\"testnet04\",\"nonce\":\"step01\",\"payload\":{\"exec\":{\"code\":\"(+ 5 6)\",\"data\":{\"accounts-admin-keyset\":[\"ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d\"]}}},\"signers\":[]}", + hash: %Kadena.Types.PactTransactionHash{ + hash: "5PF8EzDCdBLiW6lSzQWi9asTYvxx6ehc3cwJ-O4O5HY" + }, + sigs: [] + }} = + path2 + |> ExecCommand.from_yaml() + |> ExecCommand.build() + end + + test "with an invalid path" do + {:error, [path: :invalid]} = ExecCommand.from_yaml(123) + end + + test "with a non existing YAML file", %{bad_path: bad_path} do + {:error, + %YamlElixir.FileNotFoundError{ + message: + "Failed to open file \"test/support/yaml_tests_files/no_existent.yaml\": no such file or directory" + }} = ExecCommand.from_yaml(bad_path) + end + + test "with an invalid meta_data", %{path3: path3} do + {:error, [metadata: :invalid]} = ExecCommand.from_yaml(path3) + end + + test "with an invalid meta_data args", %{path4: path4} do + {:error, [meta_data: :invalid, gas_price: :invalid]} = ExecCommand.from_yaml(path4) + end + + test "with an invalid keypair", %{path5: path5} do + {:error, [keypair: :invalid]} = ExecCommand.from_yaml(path5) + end + + test "with an invalid keypair args", %{path6: path6} do + {:error, [keypair: :invalid, pub_key: :invalid]} = ExecCommand.from_yaml(path6) + end + + test "with an invalid signers", %{path7: path7} do + {:error, [signers: :invalid]} = ExecCommand.from_yaml(path7) + end + + test "with an invalid signers args", %{path8: path8} do + {:error, [signers: :invalid, scheme: :invalid]} = ExecCommand.from_yaml(path8) + end + end + describe "create ExecCommand" do setup do secret_key = "99f7e1e8f2f334ae8374aa28bebdb997271a0e0a5e92c80be9609684a3d6f0d4" {:ok, %KeyPair{pub_key: pub_key}} = CryptographyKeyPair.from_secret_key(secret_key) - cap = Cap.new(%{name: "coin.GAS", args: [pub_key]}) + cap = Cap.new(name: "coin.GAS", args: [pub_key]) clist = [cap] keypair_data = [pub_key: pub_key, secret_key: secret_key, clist: clist] diff --git a/test/support/fixtures/chainweb/send_2.json b/test/support/fixtures/chainweb/send_2.json new file mode 100644 index 0000000..8ffb0d4 --- /dev/null +++ b/test/support/fixtures/chainweb/send_2.json @@ -0,0 +1,3 @@ +{ + "requestKeys": ["64rRUBVnKEJOw0-zjqqAoFz5KRPHN95uPRt9hNi44jM"] +} diff --git a/test/support/yaml_tests_files/for_test_exec.yaml b/test/support/yaml_tests_files/for_test_exec.yaml new file mode 100644 index 0000000..9d7a002 --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec.yaml @@ -0,0 +1,24 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - 6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7 +nonce: 2023-06-13 17:45:18.211131 UTC +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 1000 + gasPrice: 0.000001 + ttl: 28800 + sender: k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94 +keyPairs: + - public: 6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7 + secret: 99f7e1e8f2f334ae8374aa28bebdb997271a0e0a5e92c80be9609684a3d6f0d4 +signers: + - publicKey: cc30ae980161eba5da95a0d27dbdef29f185a23406942059c16cb120f6dc9dea + scheme: :ed25519 + addr: cc30ae980161eba5da95a0d27dbdef29f185a23406942059c16cb120f6dc9dea + capsList: + - name: coin.GAS + args: + - 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 diff --git a/test/support/yaml_tests_files/for_test_exec_2.yaml b/test/support/yaml_tests_files/for_test_exec_2.yaml new file mode 100644 index 0000000..c38377c --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_2.yaml @@ -0,0 +1,6 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 diff --git a/test/support/yaml_tests_files/for_test_exec_3.yaml b/test/support/yaml_tests_files/for_test_exec_3.yaml new file mode 100644 index 0000000..d200791 --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_3.yaml @@ -0,0 +1,7 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 +publicMeta: unknown diff --git a/test/support/yaml_tests_files/for_test_exec_4.yaml b/test/support/yaml_tests_files/for_test_exec_4.yaml new file mode 100644 index 0000000..1a0b837 --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_4.yaml @@ -0,0 +1,24 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 2500 + gasPrice: invalid_value + ttl: 7200 + sender: "k:37e60c00779cacaef1f0a8697387a5945ef3cb82963980db486dc26ec5f424d9" +keyPairs: + - public: ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d + secret: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 +signers: + - publicKey: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 + scheme: :ed25519 + addr: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 + capsList: + - name: coin.GAS + args: + - 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 diff --git a/test/support/yaml_tests_files/for_test_exec_5.yaml b/test/support/yaml_tests_files/for_test_exec_5.yaml new file mode 100644 index 0000000..4c9578d --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_5.yaml @@ -0,0 +1,14 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 2500 + gasPrice: 0.1 + ttl: 7200 + sender: "k:37e60c00779cacaef1f0a8697387a5945ef3cb82963980db486dc26ec5f424d9" +keyPairs: unknown diff --git a/test/support/yaml_tests_files/for_test_exec_6.yaml b/test/support/yaml_tests_files/for_test_exec_6.yaml new file mode 100644 index 0000000..a7c6519 --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_6.yaml @@ -0,0 +1,24 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 2500 + gasPrice: 0.1 + ttl: 7200 + sender: "k:37e60c00779cacaef1f0a8697387a5945ef3cb82963980db486dc26ec5f424d9" +keyPairs: + - public: [unknown] + secret: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 +signers: + - publicKey: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 + scheme: :ed25519 + addr: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 + capsList: + - name: coin.GAS + args: + - 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 diff --git a/test/support/yaml_tests_files/for_test_exec_7.yaml b/test/support/yaml_tests_files/for_test_exec_7.yaml new file mode 100644 index 0000000..c6150dd --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_7.yaml @@ -0,0 +1,17 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 2500 + gasPrice: 0.1 + ttl: 7200 + sender: "k:37e60c00779cacaef1f0a8697387a5945ef3cb82963980db486dc26ec5f424d9" +keyPairs: + - public: ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d + secret: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 +signers: unknown diff --git a/test/support/yaml_tests_files/for_test_exec_8.yaml b/test/support/yaml_tests_files/for_test_exec_8.yaml new file mode 100644 index 0000000..0711388 --- /dev/null +++ b/test/support/yaml_tests_files/for_test_exec_8.yaml @@ -0,0 +1,24 @@ +networkId: :testnet04 +code: "(+ 5 6)" +data: + accounts-admin-keyset: + - ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d +nonce: step01 +publicMeta: + creationTime: 1667249173 + chainId: "0" + gasLimit: 2500 + gasPrice: 0.1 + ttl: 7200 + sender: "k:37e60c00779cacaef1f0a8697387a5945ef3cb82963980db486dc26ec5f424d9" +keyPairs: + - public: ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d + secret: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 +signers: + - publicKey: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 + scheme: unknown + addr: 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 + capsList: + - name: coin.GAS + args: + - 8693e641ae2bbe9ea802c736f42027b03f86afe63cae315e7169c9c496c17332 diff --git a/test/support/yaml_tests_files/for_test_send.yaml b/test/support/yaml_tests_files/for_test_send.yaml new file mode 100644 index 0000000..acb3fb6 --- /dev/null +++ b/test/support/yaml_tests_files/for_test_send.yaml @@ -0,0 +1,13 @@ +networkId: :testnet04 +code: "(+ 1 10)" +nonce: "2022-01-01 00:00:00.000000 UTC" +publicMeta: + creationTime: 1675093790 + chainId: "1" + gasLimit: 1000 + gasPrice: 0.000001 + ttl: 28800 + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7" +keyPairs: + - public: d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7 + secret: 28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c diff --git a/test/types/cap_test.exs b/test/types/cap_test.exs index b0ae2d5..fdf00cd 100644 --- a/test/types/cap_test.exs +++ b/test/types/cap_test.exs @@ -23,7 +23,7 @@ defmodule Kadena.Types.CapTest do %Cap{ name: "gas", args: [%PactValue{literal: "COIN.gas"}, %PactValue{literal: ^decimal}] - } = Cap.new(%{name: "gas", args: ["COIN.gas", 1.0e-2]}) + } = Cap.new(name: "gas", args: ["COIN.gas", 1.0e-2]) end test "with valid pact value list" do diff --git a/test/types/keypair_test.exs b/test/types/keypair_test.exs index d082650..2c1eda6 100644 --- a/test/types/keypair_test.exs +++ b/test/types/keypair_test.exs @@ -8,14 +8,16 @@ defmodule Kadena.Types.KeypairTest do alias Kadena.Types.{Cap, KeyPair} setup do - cap1 = Cap.new(%{name: "gas", args: ["COIN.gas", 0.02]}) - cap2 = Cap.new(%{name: "transfer", args: ["COIN.transfer", "key_1", 50, "key_2"]}) + cap1 = Cap.new(name: "gas", args: ["COIN.gas", 0.02]) + cap2 = Cap.new(name: "transfer", args: ["COIN.transfer", "key_1", 50, "key_2"]) clist = [cap1, cap2] + clist2 = [cap1] %{ pub_key: "ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d", secret_key: "99f7e1e8f2f334ae8374aa28bebdb997271a0e0a5e92c80be9609684a3d6f0d4", clist: clist, + clist2: clist2, invalid_key: "ba54b224d1924dd98403f5c751abd" } end @@ -29,6 +31,31 @@ defmodule Kadena.Types.KeypairTest do } = KeyPair.new(pub_key: pub_key, secret_key: secret_key, clist: clist) end + test "with valid map arguments", %{pub_key: pub_key, secret_key: secret_key} do + %KeyPair{ + pub_key: ^pub_key, + secret_key: ^secret_key, + clist: nil + } = KeyPair.new(%{"public" => pub_key, "secret" => secret_key}) + end + + test "with caps_data map from map params", %{ + pub_key: pub_key, + secret_key: secret_key, + clist2: clist2 + } do + %KeyPair{ + pub_key: ^pub_key, + secret_key: ^secret_key, + clist: ^clist2 + } = + KeyPair.new(%{ + "public" => pub_key, + "secret" => secret_key, + "capsList" => [%{"name" => "gas", "args" => ["COIN.gas", 0.02]}] + }) + end + test "with only required arguments", %{pub_key: pub_key, secret_key: secret_key} do %KeyPair{pub_key: ^pub_key, secret_key: ^secret_key, clist: nil} = KeyPair.new(pub_key: pub_key, secret_key: secret_key) @@ -61,7 +88,7 @@ defmodule Kadena.Types.KeypairTest do end test "with an invalid KeywordList" do - {:error, [args: :not_a_list]} = KeyPair.new("invalid_args") + {:error, [args: :invalid]} = KeyPair.new("invalid_args") end end end diff --git a/test/types/meta_data_test.exs b/test/types/meta_data_test.exs index 375ebb9..d80e472 100644 --- a/test/types/meta_data_test.exs +++ b/test/types/meta_data_test.exs @@ -8,7 +8,7 @@ defmodule Kadena.Types.MetaDataTest do use ExUnit.Case describe "new/1" do - test "with a valid param list" do + test "with a valid list params" do %MetaData{ creation_time: 0, ttl: 0, @@ -27,6 +27,25 @@ defmodule Kadena.Types.MetaDataTest do ) end + test "with a valid map params" do + %MetaData{ + creation_time: 0, + ttl: 0, + gas_limit: 2500, + gas_price: 1.0e-2, + sender: "368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca", + chain_id: %ChainID{id: "0"} + } = + MetaData.new(%{ + "creationTime" => 0, + "ttl" => 0, + "gasLimit" => 2500, + "gasPrice" => 1.0e-2, + "sender" => "368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca", + "chainId" => "0" + }) + end + test "with an invalid cration_time" do {:error, [creation_time: :invalid]} = MetaData.new( @@ -100,7 +119,7 @@ defmodule Kadena.Types.MetaDataTest do end test "with an invalid KeywordList" do - {:error, [args: :not_a_list]} = MetaData.new("invalid_args") + {:error, [args: :invalid]} = MetaData.new("invalid_args") end end end diff --git a/test/types/signer_test.exs b/test/types/signer_test.exs index 79e2153..56c3931 100644 --- a/test/types/signer_test.exs +++ b/test/types/signer_test.exs @@ -9,17 +9,20 @@ defmodule Kadena.Types.SignerTest do describe "new/1" do setup do - cap = Cap.new(name: "gas", args: ["COIN.gas", 0.02]) + cap_data = [name: "gas", args: ["COIN.gas", 0.02]] + cap = Cap.new(cap_data) caps_list = [cap, cap, cap] + caps_list2 = [cap] %{ base16string: "64617373646164617364617364616473616461736461736464", signer_scheme: :ed25519, - caps_list: caps_list + caps_list: caps_list, + caps_list2: caps_list2 } end - test "with valid params", %{ + test "with valid list params", %{ base16string: base16string, signer_scheme: signer_scheme, caps_list: caps_list @@ -38,6 +41,43 @@ defmodule Kadena.Types.SignerTest do ) end + test "with valid map params", %{ + base16string: base16string, + signer_scheme: signer_scheme + } do + %Signer{ + pub_key: %Base16String{value: ^base16string}, + scheme: ^signer_scheme, + addr: %Base16String{value: ^base16string}, + clist: nil + } = + Signer.new(%{ + "publicKey" => base16string, + "scheme" => signer_scheme, + "addr" => base16string, + "clist" => nil + }) + end + + test "with caps_data map from map params", %{ + base16string: base16string, + signer_scheme: signer_scheme, + caps_list2: caps_list2 + } do + %Signer{ + pub_key: %Base16String{value: ^base16string}, + scheme: ^signer_scheme, + addr: %Base16String{value: ^base16string}, + clist: ^caps_list2 + } = + Signer.new(%{ + "publicKey" => base16string, + "scheme" => signer_scheme, + "addr" => base16string, + "capsList" => [%{"name" => "gas", "args" => ["COIN.gas", 0.02]}] + }) + end + test "with only the required valid params", %{base16string: base16string} do %Signer{ pub_key: %Base16String{value: ^base16string}, From 4fd1285ec9c553073d76c3d41572aaa232c2d7d9 Mon Sep 17 00:00:00 2001 From: Edwin Steven Guayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Tue, 31 Jan 2023 14:16:48 -0500 Subject: [PATCH 3/3] Prepare release v0.18.0 (#271) --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- mix.exs | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b64753..89bdb8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.18.0 (31.01.2023) + +- Added [YAML reader](https://github.com/kommitters/kadena.ex/issues/253) +- Added [YAML Execution command](https://github.com/kommitters/kadena.ex/issues/208) + ## 0.17.1 (25.01.2023) - Updated README diff --git a/README.md b/README.md index 30da3e9..be9b616 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Add `kadena` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:kadena, "~> 0.17.1"} + {:kadena, "~> 0.18.0"} ] end ``` @@ -1839,7 +1839,7 @@ Chainweb.Peer.new() ## Roadmap -The latest updated branch to target a PR is `v0.18` +The latest updated branch to target a PR is `v0.19` You can see a big picture of the roadmap here: [**ROADMAP**][roadmap] diff --git a/mix.exs b/mix.exs index f035ab0..4aa2318 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Kadena.MixProject do use Mix.Project - @version "0.17.1" + @version "0.18.0" @github_url "https://github.com/kommitters/kadena.ex" def project do @@ -88,7 +88,8 @@ defmodule Kadena.MixProject do Kadena.Pact.Command, Kadena.Pact.Command.Hash, Kadena.Pact.ExecCommand, - Kadena.Pact.ContCommand + Kadena.Pact.ContCommand, + Kadena.Pact.YamlReader ], "Kadena Chainweb": [ Kadena.Chainweb.Client,