Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add YAML continuation command implementation #272

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 107 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,10 @@ 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.
- `codeFile`: The name of a `pact` file in the same directory as the `YAML` file, ex: `code.pact`.
- `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.
- `dataFile`: The name of a `json` file in the same directory as the `YAML` file, ex: `data.json`.
- `nonce`: [Nonce](#nonce) value.
- `publicMeta`: [Metadata](#metadata) value.
- `keyPairs`: [KeyPairs](#keypairs) values.
Expand Down Expand Up @@ -445,6 +445,10 @@ alias Kadena.Pact.ExecCommand

### Building a Continuation Command

There are two ways to create a ContCommand.

#### Using [attributes](#attributes) structures

```elixir
alias Kadena.Cryptography
alias Kadena.Pact
Expand Down Expand Up @@ -508,6 +512,107 @@ rollback = true
}}
```

#### With a `YAML` file
EdwinGuayacan marked this conversation as resolved.
Show resolved Hide resolved

YAML struct:

- `networkId`: [NetworkID](#networkid) value.
- `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, ex: `"data.json"`.
- `nonce`: [Nonce](#nonce) value.
- `publicMeta`: [Metadata](#metadata) value.
- `keyPairs`: [KeyPairs](#keypairs) values.
- `signers`: [Signers](#signers) values.
- `pactTxHash`: [PactTxHash](#pacttxhash-continuation-command) value.
- `rollback`: [Rollback](#rollback-continuation-command) value.
- `Step`: [Step](#step-continuation-command) value.
- `proof`: [Proof](#proof-continuation-command) value.


The scheme below shows how to set the different values of a `ContCommand`

```YAML
networkId:
data/dataFile:
nonce:
publicMeta:
creationTime:
chainId:
gasLimit:
gasPrice:
ttl:
sender:
keyPairs:
- public:
secret:
signers:
- publicKey:
scheme:
addr:
capsList:
- name:
args:
-
pactTxHash:
step:
rollback:
proof:

```
**Example**

YAML file:
```YAML
networkId: :testnet04
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
pactTxHash: yxM0umrtdcvSUZDc_GSjwadH6ELYFCjOqI59Jzqapi4
step: 1
rollback: true

```

```elixir
alias Kadena.Pact.ContCommand

"~/your_file_path"
|> ContCommand.from_yaml()
|> ContCommand.build()
EdwinGuayacan marked this conversation as resolved.
Show resolved Hide resolved

{: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\":{\"cont\":{\"data\":{\"accounts_admin_keyset\":[\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\"]},\"pactId\":\"\",\"proof\":null,\"rollback\":true,\"step\":0}},\"signers\":[{\"addr\":null,\"clist\":[{\"args\":[\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\",\"scheme\":\"ED25519\"}]}",
hash: %Kadena.Types.PactTransactionHash{
hash: "psIXOGGneMAV1Ie3zx5O1VWMFueFZrShvaBx4YOCkjQ"
},
sigs: [
%Kadena.Types.Signature{
sig:
"1ae6e796bbf8e1ddb005945508ac6fd13cc6435c4f63609cff299114865fd13879b8b5bcad13383ae377acc10411e49e745397320a2ba5bf9d1370cafbf90a06"
}
]
}}

```

## Chainweb Pact API

Interaction with [Chainweb Pact API][chainweb_pact_api_doc] is done through the [**Kadena.Chainweb.Pact**][chainweb_pact_api] module using simple functions to access endpoints.
Expand Down
75 changes: 73 additions & 2 deletions lib/pact/command/cont_command.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Kadena.Pact.ContCommand 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,
Expand Down Expand Up @@ -103,7 +103,34 @@ defmodule Kadena.Pact.ContCommand do
def new(_opts), do: %__MODULE__{}

@impl true
def from_yaml(_path), do: new()
def from_yaml(path) when is_binary(path) do
with {:ok, map_result} <- YamlReader.read(path) do
network_id = Map.get(map_result, "networkId")
data = Map.get(map_result, "data")
nonce = Map.get(map_result, "nonce", "")
meta_data = Map.get(map_result, "publicMeta", MetaData.new())
pact_tx_hash = Map.get(map_result, "pactTxHash", "")
step = Map.get(map_result, "step", 0)
proof = Map.get(map_result, "proof")
rollback = Map.get(map_result, "rollback", true)
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_nonce(nonce)
|> set_pact_tx_hash(pact_tx_hash)
|> set_proof(proof)
|> set_step(step)
|> set_rollback(rollback)
end
end

def from_yaml(_path), do: {:error, [path: :invalid]}

@impl true
def set_network(%__MODULE__{} = cmd_request, network) do
Expand Down Expand Up @@ -313,4 +340,48 @@ defmodule Kadena.Pact.ContCommand 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
100 changes: 86 additions & 14 deletions test/pact/command/cont_command_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,98 @@ defmodule Kadena.Pact.ContCommandTest do

describe "create ContCommand with YAMl file" do
setup do
path = ""
path = "test/support/yaml_tests_files/for_test_cont.yaml"
path2 = "test/support/yaml_tests_files/for_test_commands_1.yaml"
path3 = "test/support/yaml_tests_files/for_test_commands_2.yaml"
path4 = "test/support/yaml_tests_files/for_test_commands_3.yaml"
path5 = "test/support/yaml_tests_files/for_test_commands_4.yaml"
path6 = "test/support/yaml_tests_files/for_test_commands_5.yaml"
path7 = "test/support/yaml_tests_files/for_test_commands_6.yaml"
path8 = "test/support/yaml_tests_files/for_test_commands_7.yaml"
bad_path = "test/support/yaml_tests_files/no_existent.yaml"

%{
path: path
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
%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)
{: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\":{\"cont\":{\"data\":{\"accounts-admin-keyset\":[\"6ffea3fabe4e7fe6a89f88fc6d662c764ed1359fbc03a28afdac3935415347d7\"]},\"pactId\":\"yxM0umrtdcvSUZDc_GSjwadH6ELYFCjOqI59Jzqapi4\",\"proof\":null,\"rollback\":true,\"step\":1}},\"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: "fcR4cObX0BegUNvQu3CeMn8FhMlufY6CYHxQa39eJuY"
},
sigs: [
%Kadena.Types.Signature{
sig:
"eef27b61796cd1668e73078af4de4cb028796291c99e54a12be5f98ba0121b4bb631b322fdd583458e5ae30d962039452676a3f3c83a1f3b4b1be2e74b188a0d"
}
]
}} =
path
|> ContCommand.from_yaml()
|> ContCommand.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\":{\"cont\":{\"data\":{\"accounts-admin-keyset\":[\"ba54b224d1924dd98403f5c751abdd10de6cd81b0121800bf7bdbdcfaec7388d\"]},\"pactId\":\"\",\"proof\":null,\"rollback\":true,\"step\":0}},\"signers\":[]}",
hash: %Kadena.Types.PactTransactionHash{
hash: "njHWk39wmyfWDKFF5GJRRlfBcdj57oTUZYKiKY3Ru4I"
},
sigs: []
}} =
path2
|> ContCommand.from_yaml()
|> ContCommand.build()
end

test "with an invalid path" do
{:error, [path: :invalid]} = ContCommand.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"
}} = ContCommand.from_yaml(bad_path)
end

test "with an invalid meta_data", %{path3: path3} do
{:error, [metadata: :invalid]} = ContCommand.from_yaml(path3)
end

test "with an invalid meta_data args", %{path4: path4} do
{:error, [meta_data: :invalid, gas_price: :invalid]} = ContCommand.from_yaml(path4)
end

test "with an invalid keypair", %{path5: path5} do
{:error, [keypair: :invalid]} = ContCommand.from_yaml(path5)
end

test "with an invalid keypair args", %{path6: path6} do
{:error, [keypair: :invalid, pub_key: :invalid]} = ContCommand.from_yaml(path6)
end

test "with an invalid signers", %{path7: path7} do
{:error, [signers: :invalid]} = ContCommand.from_yaml(path7)
end

test "with an invalid signers args", %{path8: path8} do
{:error, [signers: :invalid, scheme: :invalid]} = ContCommand.from_yaml(path8)
end
end

Expand Down
14 changes: 7 additions & 7 deletions test/pact/command/exec_command_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ defmodule Kadena.Pact.ExecCommandTest do
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"
path2 = "test/support/yaml_tests_files/for_test_commands_1.yaml"
path3 = "test/support/yaml_tests_files/for_test_commands_2.yaml"
path4 = "test/support/yaml_tests_files/for_test_commands_3.yaml"
path5 = "test/support/yaml_tests_files/for_test_commands_4.yaml"
path6 = "test/support/yaml_tests_files/for_test_commands_5.yaml"
path7 = "test/support/yaml_tests_files/for_test_commands_6.yaml"
path8 = "test/support/yaml_tests_files/for_test_commands_7.yaml"
bad_path = "test/support/yaml_tests_files/no_existent.yaml"

%{
Expand Down
26 changes: 26 additions & 0 deletions test/support/yaml_tests_files/for_test_cont.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
networkId: :testnet04
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
pactTxHash: yxM0umrtdcvSUZDc_GSjwadH6ELYFCjOqI59Jzqapi4
step: 1
rollback: true