From e939aa3edf73e975edce2d55835fa6198f2fd62e Mon Sep 17 00:00:00 2001 From: Edwin Steven Guayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Thu, 15 Dec 2022 18:46:26 -0500 Subject: [PATCH 1/6] Add local endpoitn and PACT API Add new function in Cap and modify JSON format (#168) * Add new function in Cap type and JSON format changes add local endpoint and pact API --- lib/chainweb/pact.ex | 13 +++ lib/chainweb/pact/endpoints.ex | 46 +++++++++ lib/chainweb/pact/local.ex | 40 ++++++++ lib/types/local_request_body.ex | 54 +++++++++++ .../chainweb/pact/local_request_body_test.exs | 93 +++++++++++++------ test/chainweb/pact/local_test.exs | 69 ++++++++++++++ 6 files changed, 287 insertions(+), 28 deletions(-) create mode 100644 lib/chainweb/pact/endpoints.ex create mode 100644 lib/chainweb/pact/local.ex create mode 100644 lib/types/local_request_body.ex create mode 100644 test/chainweb/pact/local_test.exs diff --git a/lib/chainweb/pact.ex b/lib/chainweb/pact.ex index c11b81c..9d951a6 100644 --- a/lib/chainweb/pact.ex +++ b/lib/chainweb/pact.ex @@ -1,5 +1,6 @@ defmodule Kadena.Chainweb.Pact do @moduledoc """ +<<<<<<< HEAD Exposes functions to interact with the Pact API endpoints. """ @@ -8,4 +9,16 @@ defmodule Kadena.Chainweb.Pact do @default_network_opts [network_id: :testnet04, chain_id: 0] defdelegate send(cmds, network_opts \\ @default_network_opts), to: Send, as: :process +======= + Specifies the API for processing HTTP requests. + """ + + alias Kadena.Chainweb.Pact.Local + defdelegate local(request, network \\ :testnet04, chain_id \\ "0"), to: Local, as: :process + + # defdelegate send(request, network \\ :test, chain_id \\ "0"), to: Send, as: :process + # defdelegate poll(request, network \\ :test, chain_id \\ "0"), to: Poll, as: :process + # defdelegate listen(request, network \\ :test, chain_id \\ "0"), to: Listen, as: :process + # defdelegate spv(request, network \\ :test, chain_id \\ "0"), to: SPV, as: :process +>>>>>>> 40795f4 (Add local endpoitn and PACT API) end diff --git a/lib/chainweb/pact/endpoints.ex b/lib/chainweb/pact/endpoints.ex new file mode 100644 index 0000000..3fd2417 --- /dev/null +++ b/lib/chainweb/pact/endpoints.ex @@ -0,0 +1,46 @@ +defmodule Kadena.Chainweb.Pact.Endpoints do + @moduledoc """ + Requests specification for `Chainweb.Pact` contracts. + """ + +<<<<<<< HEAD:lib/chainweb/pact/request.ex +======= + alias Kadena.Types.Command + alias Kadena.Types.CommandsList + + alias Kadena.Types.{ + Command, + CommandsList + } + +>>>>>>> 4e12645 (Add local endpoitn and PACT API):lib/chainweb/pact/endpoints.ex + alias Kadena.Chainweb.Pact.{ + ListenRequestBody, + ListenResponse, + LocalRequestBody, + LocalResponse, + PollRequestBody, + PollResponse, + SendRequestBody, + SendResponse, + SPVRequestBody, + SPVResponse + } + + @type network :: atom() + @type chain_id :: String.t() + @type error :: {:error, Keyword.t()} + @type body :: String.t() + @type request :: + Command.t() + | CommandsList.t() + @type response :: + ListenResponse.t() + | LocalResponse.t() + | PollResponse.t() + | SendResponse.t() + | SPVResponse.t() + + @callback process(request :: request(), network :: network(), chain_id :: chain_id()) :: + {:ok, response()} | error() +end diff --git a/lib/chainweb/pact/local.ex b/lib/chainweb/pact/local.ex new file mode 100644 index 0000000..c98679e --- /dev/null +++ b/lib/chainweb/pact/local.ex @@ -0,0 +1,40 @@ +defmodule Kadena.Chainweb.Pact.Local do + @moduledoc """ + Send endpoint implementation + """ + + alias Kadena.Chainweb.Request + alias Kadena.Chainweb.Pact.{Endpoints, JSONPayload, LocalResponse} + alias Kadena.Types.{Command, LocalRequestBody} + + @type cmd :: Command.t() + @type json_request_body :: String.t() + @behaviour Endpoints + + @endpoint "local" + @headers [{"Content-Type", "application/json"}] + + @impl true + + def process(%Command{} = cmd, network, chain_id) do + request_body = get_json_request_body(cmd) + + :post + |> Request.new(pact: [endpoint: @endpoint]) + |> Request.set_chain_id(chain_id) + |> Request.set_network(network) + |> Request.add_body(request_body) + |> Request.add_headers(@headers) + |> Request.perform() + |> Request.results(as: LocalResponse) + end + + def process(_cmds, _network, _chain_id), do: {:error, [command: :invalid_command]} + + @spec get_json_request_body(cmd :: cmd()) :: json_request_body() + defp get_json_request_body(cmd) do + cmd + |> LocalRequestBody.new() + |> JSONPayload.parse() + end +end diff --git a/lib/types/local_request_body.ex b/lib/types/local_request_body.ex new file mode 100644 index 0000000..2eeeb80 --- /dev/null +++ b/lib/types/local_request_body.ex @@ -0,0 +1,54 @@ +defmodule Kadena.Types.LocalRequestBody do + @moduledoc """ + `LocalRequestBody` struct definition. + """ + + alias Kadena.Chainweb.Pact.JSONPayload + alias Kadena.Types.{Command, PactTransactionHash, SignaturesList} + + @behaviour Kadena.Types.Spec + + @type command :: Command.t() + @type hash :: PactTransactionHash.t() + @type sigs :: SignaturesList.t() + @type cmd :: String.t() + @type errors :: {:error, Keyword.t()} + + @type t :: %__MODULE__{hash: hash(), sigs: sigs(), cmd: cmd()} + + defstruct [:hash, :sigs, :cmd] + + @impl true + def new(%Command{} = command), do: build_local_request_body(command) + def new(_any), do: {:error, [arg: :not_a_command]} + + @spec build_local_request_body(command :: command()) :: t() + defp build_local_request_body(%Command{} = command) do + attrs = Map.from_struct(command) + struct(%__MODULE__{}, attrs) + end + + defimpl JSONPayload do + alias Kadena.Types.LocalRequestBody + alias Kadena.Utils.MapCase + + @type signatures_list :: SignaturesList.t() + @type signatures :: list(map()) + + @impl true + def parse(%LocalRequestBody{hash: hash, sigs: sigs, cmd: cmd}) do + with %PactTransactionHash{hash: hash} <- hash, + {:ok, sigs} <- to_signature_list(sigs) do + %{hash: hash, sigs: sigs, cmd: cmd} + |> MapCase.to_camel!() + |> Jason.encode!() + end + end + + @spec to_signature_list(signatures :: signatures_list()) :: {:ok, signatures()} + defp to_signature_list(%SignaturesList{signatures: list}) do + sigs = Enum.map(list, fn sig -> Map.from_struct(sig) end) + {:ok, sigs} + end + end +end diff --git a/test/chainweb/pact/local_request_body_test.exs b/test/chainweb/pact/local_request_body_test.exs index 1088bcc..082df61 100644 --- a/test/chainweb/pact/local_request_body_test.exs +++ b/test/chainweb/pact/local_request_body_test.exs @@ -7,64 +7,101 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBodyTest do alias Kadena.Chainweb.Pact.LocalRequestBody +<<<<<<< HEAD:test/chainweb/pact/local_request_body_test.exs alias Kadena.Types.{PactTransactionHash, SignaturesList} +======= + alias Kadena.Types.{ + Command, + LocalRequestBody, + PactTransactionHash, + Signature, + SignaturesList + } +>>>>>>> 4e12645 (Add local endpoitn and PACT API):test/types/local_request_body_test.exs setup do + 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\":{}}},\"signers\":[{\"addr\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"clist\":[{\"args\":[\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"scheme\":\"ED25519\"}]}" + + hash = %PactTransactionHash{ + hash: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk" + } + + sigs = %SignaturesList{ + signatures: [ + %Signature{ + sig: + "8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d" + } + ] + } + + command = %Command{ + cmd: cmd, + hash: hash, + sigs: sigs + } + %{ - hash: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk", - sigs: [ - "8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d" - ], - 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\":{}}},\"signers\":[{\"addr\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"clist\":[{\"args\":[\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"scheme\":\"ED25519\"}]}" + command: command, + cmd: cmd, + hash: hash, + sigs: sigs } end describe "new/1" do - test "with a valid params list", %{hash: hash, sigs: sigs, cmd: cmd} do + test "with a valid params list", %{command: command, hash: hash, sigs: sigs, cmd: cmd} do %LocalRequestBody{ cmd: ^cmd, - hash: %PactTransactionHash{hash: ^hash}, - sigs: %SignaturesList{} - } = LocalRequestBody.new(hash: hash, sigs: sigs, cmd: cmd) + hash: ^hash, + sigs: ^sigs + } = LocalRequestBody.new(command) end - test "with an invalid no list params" do - {:error, [local_request_body: :not_a_list]} = LocalRequestBody.new("No list") - end - - test "with an invalid cmd", %{hash: hash, sigs: sigs} do - {:error, [cmd: :not_a_string]} = LocalRequestBody.new(hash: hash, sigs: sigs, cmd: 123) - end - - test "with an invalid hash", %{sigs: sigs, cmd: cmd} do - {:error, [hash: :invalid]} = LocalRequestBody.new(hash: 123, sigs: sigs, cmd: cmd) - end - - test "with an invalid sigs list", %{hash: hash, cmd: cmd} do - {:error, [sigs: :invalid, signatures: :invalid, sig: :invalid]} = - LocalRequestBody.new(hash: hash, sigs: [invalid_signature: :invalid_value], cmd: cmd) + test "with an invalid command", %{hash: hash, sigs: sigs} do + {:error, [arg: :not_a_command]} = LocalRequestBody.new(hash: hash, sigs: sigs, cmd: 123) end end describe "JSONPayload.parse/1" do setup do + command = %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\":{}}},\"signers\":[{\"addr\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"clist\":[{\"args\":[\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"scheme\":\"ED25519\"}]}", + hash: %PactTransactionHash{ + hash: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk" + }, + sigs: %SignaturesList{ + signatures: [ + %Signature{ + sig: + "8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d" + } + ] + } + } + %{ + command: command, json_result: "{\"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\\\":{}}},\\\"signers\\\":[{\\\"addr\\\":\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\",\\\"clist\\\":[{\\\"args\\\":[\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\"],\\\"name\\\":\\\"coin.GAS\\\"}],\\\"pubKey\\\":\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\",\\\"scheme\\\":\\\"ED25519\\\"}]}\",\"hash\":\"-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk\",\"sigs\":[{\"sig\":\"8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d\"}]}" } end test "with a valid LocalRequestBody", %{ - hash: hash, - sigs: sigs, - cmd: cmd, + command: command, json_result: json_result } do +<<<<<<< HEAD:test/chainweb/pact/local_request_body_test.exs ^json_result = [hash: hash, sigs: sigs, cmd: cmd] |> LocalRequestBody.new() |> LocalRequestBody.to_json!() +======= + local_request_body = LocalRequestBody.new(command) + ^json_result = JSONPayload.parse(local_request_body) +>>>>>>> 4e12645 (Add local endpoitn and PACT API):test/types/local_request_body_test.exs end end end diff --git a/test/chainweb/pact/local_test.exs b/test/chainweb/pact/local_test.exs new file mode 100644 index 0000000..a21f904 --- /dev/null +++ b/test/chainweb/pact/local_test.exs @@ -0,0 +1,69 @@ +defmodule Kadena.Chainweb.Pact.LocalTest do + use ExUnit.Case + + alias Kadena.Chainweb.Pact + + alias Kadena.Types.{ + Command, + CommandsList, + PactTransactionHash, + Signature, + SignaturesList + } + + describe "process/3" do + setup do + exec_command = %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\":{}}},\"signers\":[{\"addr\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"clist\":[{\"args\":[\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"scheme\":\"ED25519\"}]}", + hash: %PactTransactionHash{ + hash: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk" + }, + sigs: %SignaturesList{ + signatures: [ + %Signature{ + sig: + "8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d" + } + ] + } + } + + %{ + exec_command: exec_command, + local_response: + {:ok, + %Kadena.Chainweb.Pact.LocalResponse{ + continuation: nil, + events: nil, + gas: 7, + logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", + meta_data: %{ + block_height: 2_820_655, + block_time: 1_671_202_287_339_852, + prev_block_hash: "gpJ3rn9hyE5eGMuooSdnlEMNuvjvGZtkV37IsEVtffw", + public_meta: %{ + chain_id: "0", + creation_time: 1_667_249_173, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94", + ttl: 28_800 + } + }, + req_key: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk", + result: %{data: 11, status: "success"}, + tx_id: nil + }} + } + end + + test "with a exec Command", %{exec_command: exec_command, local_response: local_response} do + ^local_response = Pact.local(exec_command) + end + + test "with a invalid Command", %{} do + {:error, [command: :invalid_command]} = Pact.local(nil) + end + end +end From 9902a3847e787f6fb498ac2a65b9a1aa1d197b37 Mon Sep 17 00:00:00 2001 From: EdwinGuayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:46:30 -0500 Subject: [PATCH 2/6] fixing tests --- lib/chainweb/pact/local.ex | 1 - test/chainweb/pact/local_test.exs | 67 ++++++++++++++++++------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/lib/chainweb/pact/local.ex b/lib/chainweb/pact/local.ex index c98679e..db30d8f 100644 --- a/lib/chainweb/pact/local.ex +++ b/lib/chainweb/pact/local.ex @@ -15,7 +15,6 @@ defmodule Kadena.Chainweb.Pact.Local do @headers [{"Content-Type", "application/json"}] @impl true - def process(%Command{} = cmd, network, chain_id) do request_body = get_json_request_body(cmd) diff --git a/test/chainweb/pact/local_test.exs b/test/chainweb/pact/local_test.exs index a21f904..2be9e8e 100644 --- a/test/chainweb/pact/local_test.exs +++ b/test/chainweb/pact/local_test.exs @@ -5,7 +5,6 @@ defmodule Kadena.Chainweb.Pact.LocalTest do alias Kadena.Types.{ Command, - CommandsList, PactTransactionHash, Signature, SignaturesList @@ -31,35 +30,49 @@ defmodule Kadena.Chainweb.Pact.LocalTest do %{ exec_command: exec_command, - local_response: - {:ok, - %Kadena.Chainweb.Pact.LocalResponse{ - continuation: nil, - events: nil, - gas: 7, - logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", - meta_data: %{ - block_height: 2_820_655, - block_time: 1_671_202_287_339_852, - prev_block_hash: "gpJ3rn9hyE5eGMuooSdnlEMNuvjvGZtkV37IsEVtffw", - public_meta: %{ - chain_id: "0", - creation_time: 1_667_249_173, - gas_limit: 1000, - gas_price: 1.0e-6, - sender: "k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94", - ttl: 28_800 - } - }, - req_key: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk", - result: %{data: 11, status: "success"}, - tx_id: nil - }} + continuation: nil, + events: nil, + gas: 7, + logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", + meta_data: %{ + # block_height: 2_815_727, + # block_time: 1_671_054_330_981_668, + # prev_block_hash: "asisNr3nuU0t357i2bxMVUiIWDVHAMtncJHtmyENbio", + public_meta: %{ + chain_id: "0", + creation_time: 1_667_249_173, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94", + ttl: 28_800 + } + }, + req_key: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk", + result: %{data: 11, status: "success"}, + tx_id: nil } end - test "with a exec Command", %{exec_command: exec_command, local_response: local_response} do - ^local_response = Pact.local(exec_command) + test "with a exec Command", %{ + exec_command: exec_command, + continuation: continuation, + events: events, + gas: gas, + logs: logs, + meta_data: meta_data, + req_key: req_key, + result: result, + tx_id: tx_id + } do + {:ok, response} = Pact.local(exec_command) + assert(continuation == response.continuation) + assert(events == response.events) + assert(gas == response.gas) + assert(logs == response.logs) + assert(meta_data.public_meta == response.meta_data.public_meta) + assert(req_key == response.req_key) + assert(result == response.result) + assert(tx_id == response.tx_id) end test "with a invalid Command", %{} do From 737a6eb9ec0e6c3b81ef2ae12193f4ceecdb4255 Mon Sep 17 00:00:00 2001 From: EdwinGuayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Tue, 20 Dec 2022 12:36:18 -0500 Subject: [PATCH 3/6] Local endpoint refactor --- lib/chainweb/pact.ex | 16 +- lib/chainweb/pact/endpoints.ex | 46 ------ lib/chainweb/pact/local.ex | 31 ++-- lib/chainweb/pact/local_request_body.ex | 16 +- lib/chainweb/pact/spec.ex | 2 +- lib/types/local_request_body.ex | 54 ------ .../chainweb/pact/local_request_body_test.exs | 40 +---- test/chainweb/pact/local_test.exs | 154 ++++++++++-------- test/support/fixtures/chainweb/local.json | 38 ++--- 9 files changed, 132 insertions(+), 265 deletions(-) delete mode 100644 lib/chainweb/pact/endpoints.ex delete mode 100644 lib/types/local_request_body.ex diff --git a/lib/chainweb/pact.ex b/lib/chainweb/pact.ex index 9d951a6..129d79d 100644 --- a/lib/chainweb/pact.ex +++ b/lib/chainweb/pact.ex @@ -1,24 +1,12 @@ defmodule Kadena.Chainweb.Pact do @moduledoc """ -<<<<<<< HEAD Exposes functions to interact with the Pact API endpoints. """ - alias Kadena.Chainweb.Pact.Send + alias Kadena.Chainweb.Pact.{Local, Send} @default_network_opts [network_id: :testnet04, chain_id: 0] defdelegate send(cmds, network_opts \\ @default_network_opts), to: Send, as: :process -======= - Specifies the API for processing HTTP requests. - """ - - alias Kadena.Chainweb.Pact.Local - defdelegate local(request, network \\ :testnet04, chain_id \\ "0"), to: Local, as: :process - - # defdelegate send(request, network \\ :test, chain_id \\ "0"), to: Send, as: :process - # defdelegate poll(request, network \\ :test, chain_id \\ "0"), to: Poll, as: :process - # defdelegate listen(request, network \\ :test, chain_id \\ "0"), to: Listen, as: :process - # defdelegate spv(request, network \\ :test, chain_id \\ "0"), to: SPV, as: :process ->>>>>>> 40795f4 (Add local endpoitn and PACT API) + defdelegate local(cmds, network_opts \\ @default_network_opts), to: Local, as: :process end diff --git a/lib/chainweb/pact/endpoints.ex b/lib/chainweb/pact/endpoints.ex deleted file mode 100644 index 3fd2417..0000000 --- a/lib/chainweb/pact/endpoints.ex +++ /dev/null @@ -1,46 +0,0 @@ -defmodule Kadena.Chainweb.Pact.Endpoints do - @moduledoc """ - Requests specification for `Chainweb.Pact` contracts. - """ - -<<<<<<< HEAD:lib/chainweb/pact/request.ex -======= - alias Kadena.Types.Command - alias Kadena.Types.CommandsList - - alias Kadena.Types.{ - Command, - CommandsList - } - ->>>>>>> 4e12645 (Add local endpoitn and PACT API):lib/chainweb/pact/endpoints.ex - alias Kadena.Chainweb.Pact.{ - ListenRequestBody, - ListenResponse, - LocalRequestBody, - LocalResponse, - PollRequestBody, - PollResponse, - SendRequestBody, - SendResponse, - SPVRequestBody, - SPVResponse - } - - @type network :: atom() - @type chain_id :: String.t() - @type error :: {:error, Keyword.t()} - @type body :: String.t() - @type request :: - Command.t() - | CommandsList.t() - @type response :: - ListenResponse.t() - | LocalResponse.t() - | PollResponse.t() - | SendResponse.t() - | SPVResponse.t() - - @callback process(request :: request(), network :: network(), chain_id :: chain_id()) :: - {:ok, response()} | error() -end diff --git a/lib/chainweb/pact/local.ex b/lib/chainweb/pact/local.ex index db30d8f..4e8d389 100644 --- a/lib/chainweb/pact/local.ex +++ b/lib/chainweb/pact/local.ex @@ -1,39 +1,38 @@ defmodule Kadena.Chainweb.Pact.Local do @moduledoc """ - Send endpoint implementation + Local endpoint implementation """ alias Kadena.Chainweb.Request - alias Kadena.Chainweb.Pact.{Endpoints, JSONPayload, LocalResponse} - alias Kadena.Types.{Command, LocalRequestBody} + alias Kadena.Chainweb.Pact.{LocalRequestBody, LocalResponse, Spec} + alias Kadena.Types.Command - @type cmd :: Command.t() - @type json_request_body :: String.t() - @behaviour Endpoints + @behaviour Spec @endpoint "local" - @headers [{"Content-Type", "application/json"}] + + @type cmd :: Command.t() + @type json :: String.t() @impl true - def process(%Command{} = cmd, network, chain_id) do - request_body = get_json_request_body(cmd) + def process(%Command{} = cmd, network_id: network_id, chain_id: chain_id) do + request_body = json_request_body(cmd) + headers = [{"Content-Type", "application/json"}] :post |> Request.new(pact: [endpoint: @endpoint]) |> Request.set_chain_id(chain_id) - |> Request.set_network(network) + |> Request.set_network(network_id) |> Request.add_body(request_body) - |> Request.add_headers(@headers) + |> Request.add_headers(headers) |> Request.perform() |> Request.results(as: LocalResponse) end - def process(_cmds, _network, _chain_id), do: {:error, [command: :invalid_command]} - - @spec get_json_request_body(cmd :: cmd()) :: json_request_body() - defp get_json_request_body(cmd) do + @spec json_request_body(cmd :: cmd()) :: json() + defp json_request_body(cmd) do cmd |> LocalRequestBody.new() - |> JSONPayload.parse() + |> LocalRequestBody.to_json!() end end diff --git a/lib/chainweb/pact/local_request_body.ex b/lib/chainweb/pact/local_request_body.ex index d2efea9..a98eacf 100644 --- a/lib/chainweb/pact/local_request_body.ex +++ b/lib/chainweb/pact/local_request_body.ex @@ -7,7 +7,7 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBody do @behaviour Kadena.Chainweb.Pact.Type - @type command :: String.t() + @type command :: Command.t() @type hash :: PactTransactionHash.t() @type sigs :: SignaturesList.t() @type cmd :: String.t() @@ -19,11 +19,8 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBody do defstruct [:hash, :sigs, :cmd] @impl true - def new(args) do - args - |> Command.new() - |> build_local_request_body() - end + def new(%Command{} = cmd), do: build_local_request_body(cmd) + def new(_cmd), do: {:error, [arg: :not_a_command]} @impl true def to_json!(%__MODULE__{hash: hash, sigs: sigs, cmd: cmd}) do @@ -33,17 +30,12 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBody do end end - @spec build_local_request_body(command :: command() | error()) :: t() | error() + @spec build_local_request_body(command :: command()) :: t() defp build_local_request_body(%Command{} = command) do attrs = Map.from_struct(command) struct(%__MODULE__{}, attrs) end - defp build_local_request_body({:error, [command: :not_a_list]}), - do: {:error, [local_request_body: :not_a_list]} - - defp build_local_request_body({:error, reason}), do: {:error, reason} - @spec to_signature_list(signatures :: sigs()) :: {:ok, raw_sigs()} defp to_signature_list(%SignaturesList{signatures: list}) do sigs = Enum.map(list, fn sig -> Map.from_struct(sig) end) diff --git a/lib/chainweb/pact/spec.ex b/lib/chainweb/pact/spec.ex index 4ffa776..47b38d1 100644 --- a/lib/chainweb/pact/spec.ex +++ b/lib/chainweb/pact/spec.ex @@ -14,7 +14,7 @@ defmodule Kadena.Chainweb.Pact.Spec do alias Kadena.Chainweb.Error alias Kadena.Types.Command - @type data :: list(Command.t()) + @type data :: list(Command.t()) | Command.t() @type error :: {:error, Error.t()} @type chain_id :: 0..19 | String.t() @type network_opts :: [network_id: atom(), chain_id: chain_id()] diff --git a/lib/types/local_request_body.ex b/lib/types/local_request_body.ex deleted file mode 100644 index 2eeeb80..0000000 --- a/lib/types/local_request_body.ex +++ /dev/null @@ -1,54 +0,0 @@ -defmodule Kadena.Types.LocalRequestBody do - @moduledoc """ - `LocalRequestBody` struct definition. - """ - - alias Kadena.Chainweb.Pact.JSONPayload - alias Kadena.Types.{Command, PactTransactionHash, SignaturesList} - - @behaviour Kadena.Types.Spec - - @type command :: Command.t() - @type hash :: PactTransactionHash.t() - @type sigs :: SignaturesList.t() - @type cmd :: String.t() - @type errors :: {:error, Keyword.t()} - - @type t :: %__MODULE__{hash: hash(), sigs: sigs(), cmd: cmd()} - - defstruct [:hash, :sigs, :cmd] - - @impl true - def new(%Command{} = command), do: build_local_request_body(command) - def new(_any), do: {:error, [arg: :not_a_command]} - - @spec build_local_request_body(command :: command()) :: t() - defp build_local_request_body(%Command{} = command) do - attrs = Map.from_struct(command) - struct(%__MODULE__{}, attrs) - end - - defimpl JSONPayload do - alias Kadena.Types.LocalRequestBody - alias Kadena.Utils.MapCase - - @type signatures_list :: SignaturesList.t() - @type signatures :: list(map()) - - @impl true - def parse(%LocalRequestBody{hash: hash, sigs: sigs, cmd: cmd}) do - with %PactTransactionHash{hash: hash} <- hash, - {:ok, sigs} <- to_signature_list(sigs) do - %{hash: hash, sigs: sigs, cmd: cmd} - |> MapCase.to_camel!() - |> Jason.encode!() - end - end - - @spec to_signature_list(signatures :: signatures_list()) :: {:ok, signatures()} - defp to_signature_list(%SignaturesList{signatures: list}) do - sigs = Enum.map(list, fn sig -> Map.from_struct(sig) end) - {:ok, sigs} - end - end -end diff --git a/test/chainweb/pact/local_request_body_test.exs b/test/chainweb/pact/local_request_body_test.exs index 082df61..2aceee2 100644 --- a/test/chainweb/pact/local_request_body_test.exs +++ b/test/chainweb/pact/local_request_body_test.exs @@ -7,17 +7,12 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBodyTest do alias Kadena.Chainweb.Pact.LocalRequestBody -<<<<<<< HEAD:test/chainweb/pact/local_request_body_test.exs - alias Kadena.Types.{PactTransactionHash, SignaturesList} -======= alias Kadena.Types.{ Command, - LocalRequestBody, PactTransactionHash, Signature, SignaturesList } ->>>>>>> 4e12645 (Add local endpoitn and PACT API):test/types/local_request_body_test.exs setup do cmd = @@ -46,7 +41,9 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBodyTest do command: command, cmd: cmd, hash: hash, - sigs: sigs + sigs: sigs, + json_result: + "{\"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\\\":{}}},\\\"signers\\\":[{\\\"addr\\\":\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\",\\\"clist\\\":[{\\\"args\\\":[\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\"],\\\"name\\\":\\\"coin.GAS\\\"}],\\\"pubKey\\\":\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\",\\\"scheme\\\":\\\"ED25519\\\"}]}\",\"hash\":\"-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk\",\"sigs\":[{\"sig\":\"8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d\"}]}" } end @@ -65,43 +62,14 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBodyTest do end describe "JSONPayload.parse/1" do - setup do - command = %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\":{}}},\"signers\":[{\"addr\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"clist\":[{\"args\":[\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"scheme\":\"ED25519\"}]}", - hash: %PactTransactionHash{ - hash: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk" - }, - sigs: %SignaturesList{ - signatures: [ - %Signature{ - sig: - "8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d" - } - ] - } - } - - %{ - command: command, - json_result: - "{\"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\\\":{}}},\\\"signers\\\":[{\\\"addr\\\":\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\",\\\"clist\\\":[{\\\"args\\\":[\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\"],\\\"name\\\":\\\"coin.GAS\\\"}],\\\"pubKey\\\":\\\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\\\",\\\"scheme\\\":\\\"ED25519\\\"}]}\",\"hash\":\"-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk\",\"sigs\":[{\"sig\":\"8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d\"}]}" - } - end - test "with a valid LocalRequestBody", %{ command: command, json_result: json_result } do -<<<<<<< HEAD:test/chainweb/pact/local_request_body_test.exs ^json_result = - [hash: hash, sigs: sigs, cmd: cmd] + command |> LocalRequestBody.new() |> LocalRequestBody.to_json!() -======= - local_request_body = LocalRequestBody.new(command) - ^json_result = JSONPayload.parse(local_request_body) ->>>>>>> 4e12645 (Add local endpoitn and PACT API):test/types/local_request_body_test.exs end end end diff --git a/test/chainweb/pact/local_test.exs b/test/chainweb/pact/local_test.exs index 2be9e8e..b37ba7a 100644 --- a/test/chainweb/pact/local_test.exs +++ b/test/chainweb/pact/local_test.exs @@ -1,82 +1,102 @@ +defmodule Kadena.Chainweb.Client.CannedLocalRequests do + @moduledoc false + + alias Kadena.Test.Fixtures.Chainweb + + def request( + :post, + "https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/1/pact/api/v1/local", + _headers, + _body, + _options + ) do + response = Chainweb.fixture("local") + {:ok, response} + end +end + defmodule Kadena.Chainweb.Pact.LocalTest do + @moduledoc """ + `Local` endpoint implementation tests. + """ use ExUnit.Case - + alias Kadena.Chainweb.Client.CannedLocalRequests alias Kadena.Chainweb.Pact - - alias Kadena.Types.{ - Command, - PactTransactionHash, - Signature, - SignaturesList - } + alias Kadena.Chainweb.Pact.LocalResponse + alias Kadena.Cryptography + alias Kadena.Pact.ExecCommand + alias Kadena.Types.MetaData describe "process/3" do setup do - exec_command = %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\":{}}},\"signers\":[{\"addr\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"clist\":[{\"args\":[\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\"],\"name\":\"coin.GAS\"}],\"pubKey\":\"85bef77ea3570387cac57da34938f246c7460dc533a67823f065823e327b2afd\",\"scheme\":\"ED25519\"}]}", - hash: %PactTransactionHash{ - hash: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk" - }, - sigs: %SignaturesList{ - signatures: [ - %Signature{ - sig: - "8b234b83570359e52188cceb301036a2a7b255171e856fd550cac687a946f18fbfc0e769fd8393dda44d6d04c31b531eaf35efb3b78b5e40fd857a743133030d" - } - ] - } - } + Application.put_env(:kadena, :http_client_impl, CannedLocalRequests) + + on_exit(fn -> + Application.delete_env(:kadena, :http_client_impl) + end) + + network_id = :testnet04 + code = "(+ 5 6)" + nonce = "2023-06-13 17:45:18.211131 UTC" + + {:ok, keypair} = + Cryptography.KeyPair.from_secret_key( + "28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c" + ) + + meta_data = + MetaData.new( + creation_time: 1_671_462_208, + ttl: 28_800, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + chain_id: "1" + ) + + cmd = + ExecCommand.new() + |> ExecCommand.set_network(network_id) + |> ExecCommand.set_code(code) + |> ExecCommand.set_nonce(nonce) + |> ExecCommand.set_metadata(meta_data) + |> ExecCommand.add_keypair(keypair) + |> ExecCommand.build() %{ - exec_command: exec_command, - continuation: nil, - events: nil, - gas: 7, - logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", - meta_data: %{ - # block_height: 2_815_727, - # block_time: 1_671_054_330_981_668, - # prev_block_hash: "asisNr3nuU0t357i2bxMVUiIWDVHAMtncJHtmyENbio", - public_meta: %{ - chain_id: "0", - creation_time: 1_667_249_173, - gas_limit: 1000, - gas_price: 1.0e-6, - sender: "k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94", - ttl: 28_800 - } - }, - req_key: "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk", - result: %{data: 11, status: "success"}, - tx_id: nil + cmd: cmd, + local_response: + {:ok, + %LocalResponse{ + continuation: nil, + events: nil, + gas: 6, + logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", + meta_data: %{ + block_height: 3_303_861, + block_time: 1_671_546_034_831_940, + prev_block_hash: "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", + public_meta: %{ + chain_id: "1", + creation_time: 1_671_462_208, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + ttl: 28_800 + } + }, + req_key: "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", + result: %{data: 11, status: "success"}, + tx_id: nil + }} } end - test "with a exec Command", %{ - exec_command: exec_command, - continuation: continuation, - events: events, - gas: gas, - logs: logs, - meta_data: meta_data, - req_key: req_key, - result: result, - tx_id: tx_id + test "with an exec Command", %{ + cmd: cmd, + local_response: local_response } do - {:ok, response} = Pact.local(exec_command) - assert(continuation == response.continuation) - assert(events == response.events) - assert(gas == response.gas) - assert(logs == response.logs) - assert(meta_data.public_meta == response.meta_data.public_meta) - assert(req_key == response.req_key) - assert(result == response.result) - assert(tx_id == response.tx_id) - end - - test "with a invalid Command", %{} do - {:error, [command: :invalid_command]} = Pact.local(nil) + ^local_response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) end end end diff --git a/test/support/fixtures/chainweb/local.json b/test/support/fixtures/chainweb/local.json index e32ac0d..31dd355 100644 --- a/test/support/fixtures/chainweb/local.json +++ b/test/support/fixtures/chainweb/local.json @@ -1,24 +1,24 @@ { - "continuation": null, - "gas": 7, + "gas": 6, + "result": { + "status": "success", + "data": 11 + }, + "reqKey": "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", "logs": "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", "metaData": { - "blockHeight": 2815727, - "blockTime": 1671054330981668, - "prevBlockHash": "asisNr3nuU0t357i2bxMVUiIWDVHAMtncJHtmyENbio", - "publicMeta": { - "chainId": "0", - "creationTime": 1667249173, - "gasLimit": 1000, - "gasPrice": 1.0e-6, - "sender": "k:554754f48b16df24b552f6832dda090642ed9658559fef9f3ee1bb4637ea7c94", - "ttl": 28800 - } - }, - "reqKey": "-1npoTU2Mi71pKE_oteJiJuHuXTXxoObJm8zzVRK2pk", - "result": { - "data": 11, - "status": "success" + "publicMeta": { + "creationTime": 1671462208, + "ttl": 28800, + "gasLimit": 1000, + "chainId": "1", + "gasPrice": 1.0e-6, + "sender": "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7" + }, + "blockTime": 1671546034831940, + "prevBlockHash": "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", + "blockHeight": 3303861 }, + "continuation": null, "txId": null -} +} \ No newline at end of file From 620e14394767f89e7bd4d6e50ca6c7baf6cc9416 Mon Sep 17 00:00:00 2001 From: EdwinGuayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Tue, 20 Dec 2022 18:13:42 -0500 Subject: [PATCH 4/6] Update docs and Readme Co-authored-by: Miguel Nieto A <39246879+miguelnietoa@users.noreply.github.com> --- README.md | 78 ++++++++++++++++++ mix.exs | 10 +-- test/chainweb/pact/local_test.exs | 126 +++++++++++++++--------------- 3 files changed, 141 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 2e377a3..572205f 100644 --- a/README.md +++ b/README.md @@ -487,6 +487,84 @@ Chainweb.Pact.send(cmds, network_id: :testnet04, chain_id: 1) }} ``` +### Local endpoint + +Executes a single command on the local server and retrieves the transaction result. Useful with code that queries from blockchain. It does not impact the blockchain when returning transaction results. + +```elixir +Kadena.Chainweb.Pact.local(cmd, network_opts \\ [network_id: :testnet04, chain_id: 0]) +``` + +**Parameters** + +- `cmd`: [PACT command](#pact-commands). +- `network_opts`: Network options. Keyword list with: + + - `network_id` (required): Allowed values: `:testnet04` `mainnet01`. + - `chain_id` (required): Allowed values: integer or string-encoded integer from 0 to 19. + + Defaults to `[network_id: :testnet04, chain_id: 0]` if not specified. + +**Example** + +```elixir +alias Kadena.Chainweb +alias Kadena.Cryptography +alias Kadena.Pact + +{:ok, keypair} = + Cryptography.KeyPair.from_secret_key( + "28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c" + ) + +network_id = :testnet04 + +metadata = + Kadena.Types.MetaData.new( + creation_time: 1_671_462_208, + ttl: 28_800, + gas_limit: 1000, + gas_price: 0.000001, + sender: "k:#{keypair.pub_key}", + chain_id: "1" + ) + +code = "(+ 1 2)" + +cmd = + Pact.ExecCommand.new() + |> Pact.ExecCommand.set_code(code) + |> Pact.ExecCommand.set_metadata(metadata) + |> Pact.ExecCommand.add_keypair(keypair) + |> Pact.ExecCommand.build() + +Chainweb.Pact.local(cmd, network_id: :testnet04, chain_id: 1) + +{:ok, + %Kadena.Chainweb.Pact.LocalResponse{ + continuation: nil, + events: nil, + gas: 5, + logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", + meta_data: %{ + block_height: 2833149, + block_time: 1671577178603103, + prev_block_hash: "7aURwajZ0pBMGEKmOUJ9oLq9MK7QiZeiDPGPb0cXs5c", + public_meta: %{ + chain_id: "1", + creation_time: 1671462208, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + ttl: 28800 + } + }, + req_key: "8qnotzzhbfe_SSmZcDVQGDpALjQjYqzYYrHc6D-2D_g", + result: %{data: 3, status: "success"}, + tx_id: nil + }} +``` + --- ## Roadmap diff --git a/mix.exs b/mix.exs index caa2690..8ef460c 100644 --- a/mix.exs +++ b/mix.exs @@ -96,6 +96,7 @@ defmodule Kadena.MixProject do Kadena.Chainweb.Client.Spec, Kadena.Chainweb.Network, Kadena.Chainweb.Pact, + Kadena.Chainweb.Pact.Local, Kadena.Chainweb.Pact.Request, Kadena.Chainweb.Pact.Send, Kadena.Chainweb.Pact.Spec, @@ -119,20 +120,15 @@ defmodule Kadena.MixProject do Kadena.Types.Cap, Kadena.Types.CapsList, Kadena.Types.ChainID, - Kadena.Types.ChainwebResponseMetaData, Kadena.Types.Command, Kadena.Types.CommandsList, Kadena.Types.ContPayload, Kadena.Types.EnvData, Kadena.Types.ExecPayload, Kadena.Types.KeyPair, - Kadena.Types.ListenRequestBody, - Kadena.Types.LocalRequestBody, Kadena.Types.MetaData, Kadena.Types.NetworkID, Kadena.Types.OptionalCapsList, - Kadena.Types.OptionalMetaData, - Kadena.Types.OptionalPactEventsList, Kadena.Types.PactCode, Kadena.Types.PactDecimal, Kadena.Types.PactInt, @@ -140,10 +136,8 @@ defmodule Kadena.MixProject do Kadena.Types.PactTransactionHash, Kadena.Types.PactValue, Kadena.Types.PactValuesList, - Kadena.Types.PollRequestBody, Kadena.Types.Proof, Kadena.Types.Rollback, - Kadena.Types.SendRequestBody, Kadena.Types.SignCommand, Kadena.Types.SignatureWithHash, Kadena.Types.Signature, @@ -153,8 +147,6 @@ defmodule Kadena.MixProject do Kadena.Types.SignersList, Kadena.Types.SigningCap, Kadena.Types.Spec, - Kadena.Types.SPVProof, - Kadena.Types.SPVRequestBody, Kadena.Types.Step ], "Chainweb Pact Types": [ diff --git a/test/chainweb/pact/local_test.exs b/test/chainweb/pact/local_test.exs index b37ba7a..9ba36c8 100644 --- a/test/chainweb/pact/local_test.exs +++ b/test/chainweb/pact/local_test.exs @@ -27,76 +27,74 @@ defmodule Kadena.Chainweb.Pact.LocalTest do alias Kadena.Pact.ExecCommand alias Kadena.Types.MetaData - describe "process/3" do - setup do - Application.put_env(:kadena, :http_client_impl, CannedLocalRequests) + setup do + Application.put_env(:kadena, :http_client_impl, CannedLocalRequests) - on_exit(fn -> - Application.delete_env(:kadena, :http_client_impl) - end) + on_exit(fn -> + Application.delete_env(:kadena, :http_client_impl) + end) - network_id = :testnet04 - code = "(+ 5 6)" - nonce = "2023-06-13 17:45:18.211131 UTC" + network_id = :testnet04 + code = "(+ 5 6)" + nonce = "2023-06-13 17:45:18.211131 UTC" - {:ok, keypair} = - Cryptography.KeyPair.from_secret_key( - "28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c" - ) + {:ok, keypair} = + Cryptography.KeyPair.from_secret_key( + "28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c" + ) - meta_data = - MetaData.new( - creation_time: 1_671_462_208, - ttl: 28_800, - gas_limit: 1000, - gas_price: 1.0e-6, - sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", - chain_id: "1" - ) + meta_data = + MetaData.new( + creation_time: 1_671_462_208, + ttl: 28_800, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + chain_id: "1" + ) - cmd = - ExecCommand.new() - |> ExecCommand.set_network(network_id) - |> ExecCommand.set_code(code) - |> ExecCommand.set_nonce(nonce) - |> ExecCommand.set_metadata(meta_data) - |> ExecCommand.add_keypair(keypair) - |> ExecCommand.build() + cmd = + ExecCommand.new() + |> ExecCommand.set_network(network_id) + |> ExecCommand.set_code(code) + |> ExecCommand.set_nonce(nonce) + |> ExecCommand.set_metadata(meta_data) + |> ExecCommand.add_keypair(keypair) + |> ExecCommand.build() - %{ - cmd: cmd, - local_response: - {:ok, - %LocalResponse{ - continuation: nil, - events: nil, - gas: 6, - logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", - meta_data: %{ - block_height: 3_303_861, - block_time: 1_671_546_034_831_940, - prev_block_hash: "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", - public_meta: %{ - chain_id: "1", - creation_time: 1_671_462_208, - gas_limit: 1000, - gas_price: 1.0e-6, - sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", - ttl: 28_800 - } - }, - req_key: "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", - result: %{data: 11, status: "success"}, - tx_id: nil - }} - } - end - - test "with an exec Command", %{ + %{ cmd: cmd, - local_response: local_response - } do - ^local_response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) - end + local_response: + {:ok, + %LocalResponse{ + continuation: nil, + events: nil, + gas: 6, + logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", + meta_data: %{ + block_height: 3_303_861, + block_time: 1_671_546_034_831_940, + prev_block_hash: "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", + public_meta: %{ + chain_id: "1", + creation_time: 1_671_462_208, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + ttl: 28_800 + } + }, + req_key: "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", + result: %{data: 11, status: "success"}, + tx_id: nil + }} + } + end + + test "process/2", %{ + cmd: cmd, + local_response: local_response + } do + ^local_response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) end end From 868134b4a3cf68a2f9046b52c7dfecdc9747ac29 Mon Sep 17 00:00:00 2001 From: EdwinGuayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Wed, 21 Dec 2022 10:03:59 -0500 Subject: [PATCH 5/6] add changes requested --- lib/chainweb/pact/local.ex | 4 +- test/chainweb/pact/local_test.exs | 90 +++++++++++++++-------- test/support/fixtures/chainweb/local.json | 28 +++---- 3 files changed, 76 insertions(+), 46 deletions(-) diff --git a/lib/chainweb/pact/local.ex b/lib/chainweb/pact/local.ex index 4e8d389..cecb673 100644 --- a/lib/chainweb/pact/local.ex +++ b/lib/chainweb/pact/local.ex @@ -16,14 +16,14 @@ defmodule Kadena.Chainweb.Pact.Local do @impl true def process(%Command{} = cmd, network_id: network_id, chain_id: chain_id) do - request_body = json_request_body(cmd) headers = [{"Content-Type", "application/json"}] + body = json_request_body(cmd) :post |> Request.new(pact: [endpoint: @endpoint]) |> Request.set_chain_id(chain_id) |> Request.set_network(network_id) - |> Request.add_body(request_body) + |> Request.add_body(body) |> Request.add_headers(headers) |> Request.perform() |> Request.results(as: LocalResponse) diff --git a/test/chainweb/pact/local_test.exs b/test/chainweb/pact/local_test.exs index 9ba36c8..159cc7e 100644 --- a/test/chainweb/pact/local_test.exs +++ b/test/chainweb/pact/local_test.exs @@ -1,17 +1,33 @@ defmodule Kadena.Chainweb.Client.CannedLocalRequests do @moduledoc false + 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/local", _headers, - _body, + body, _options ) do - response = Chainweb.fixture("local") - {:ok, response} + case String.contains?(body, "(coin.get-balance 'bad')") do + true -> + response = + Error.new( + {:chainweb, + %{ + status: 400, + title: "Validation failed: Invalid command: Failed reading: empty" + }} + ) + + {:error, response} + + false -> + response = Chainweb.fixture("local") + {:ok, response} + end end end @@ -21,7 +37,7 @@ defmodule Kadena.Chainweb.Pact.LocalTest do """ use ExUnit.Case alias Kadena.Chainweb.Client.CannedLocalRequests - alias Kadena.Chainweb.Pact + alias Kadena.Chainweb.{Error, Pact} alias Kadena.Chainweb.Pact.LocalResponse alias Kadena.Cryptography alias Kadena.Pact.ExecCommand @@ -34,36 +50,14 @@ defmodule Kadena.Chainweb.Pact.LocalTest do Application.delete_env(:kadena, :http_client_impl) end) - network_id = :testnet04 code = "(+ 5 6)" - nonce = "2023-06-13 17:45:18.211131 UTC" - - {:ok, keypair} = - Cryptography.KeyPair.from_secret_key( - "28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c" - ) - - meta_data = - MetaData.new( - creation_time: 1_671_462_208, - ttl: 28_800, - gas_limit: 1000, - gas_price: 1.0e-6, - sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", - chain_id: "1" - ) - - cmd = - ExecCommand.new() - |> ExecCommand.set_network(network_id) - |> ExecCommand.set_code(code) - |> ExecCommand.set_nonce(nonce) - |> ExecCommand.set_metadata(meta_data) - |> ExecCommand.add_keypair(keypair) - |> ExecCommand.build() + bad_code = "(coin.get-balance 'bad')" + cmd = create_command(code) + cmd_for_error = create_command(bad_code) %{ cmd: cmd, + cmd_for_error: cmd_for_error, local_response: {:ok, %LocalResponse{ @@ -97,4 +91,40 @@ defmodule Kadena.Chainweb.Pact.LocalTest do } do ^local_response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) end + + test "process/2 error", %{cmd_for_error: cmd_for_error} do + {:error, + %Error{ + status: 400, + title: "Validation failed: Invalid command: Failed reading: empty" + }} = Pact.local(cmd_for_error, network_id: :testnet04, chain_id: 1) + end + + defp create_command(code) do + network_id = :testnet04 + nonce = "2023-06-13 17:45:18.211131 UTC" + + {:ok, keypair} = + Cryptography.KeyPair.from_secret_key( + "28834b7a0d6d1f84ae2c2efcb5b1de28122e07e2e4caad04a32988a3c79c547c" + ) + + meta_data = + MetaData.new( + creation_time: 1_671_462_208, + ttl: 28_800, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + chain_id: "1" + ) + + ExecCommand.new() + |> ExecCommand.set_network(network_id) + |> ExecCommand.set_code(code) + |> ExecCommand.set_nonce(nonce) + |> ExecCommand.set_metadata(meta_data) + |> ExecCommand.add_keypair(keypair) + |> ExecCommand.build() + end end diff --git a/test/support/fixtures/chainweb/local.json b/test/support/fixtures/chainweb/local.json index 31dd355..f4903a4 100644 --- a/test/support/fixtures/chainweb/local.json +++ b/test/support/fixtures/chainweb/local.json @@ -1,24 +1,24 @@ { "gas": 6, "result": { - "status": "success", - "data": 11 + "status": "success", + "data": 11 }, "reqKey": "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", "logs": "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", "metaData": { - "publicMeta": { - "creationTime": 1671462208, - "ttl": 28800, - "gasLimit": 1000, - "chainId": "1", - "gasPrice": 1.0e-6, - "sender": "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7" - }, - "blockTime": 1671546034831940, - "prevBlockHash": "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", - "blockHeight": 3303861 + "publicMeta": { + "creationTime": 1671462208, + "ttl": 28800, + "gasLimit": 1000, + "chainId": "1", + "gasPrice": 1.0e-6, + "sender": "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7" + }, + "blockTime": 1671546034831940, + "prevBlockHash": "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", + "blockHeight": 3303861 }, "continuation": null, "txId": null -} \ No newline at end of file +} From 681140d007aabe6dd01c5562e0820b46a1fcd82d Mon Sep 17 00:00:00 2001 From: EdwinGuayacan <80716239+EdwinGuayacan@users.noreply.github.com> Date: Wed, 21 Dec 2022 16:22:26 -0500 Subject: [PATCH 6/6] Refactor test setup --- test/chainweb/pact/local_test.exs | 85 ++++++++++++----------- test/support/fixtures/chainweb/local.json | 2 +- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/test/chainweb/pact/local_test.exs b/test/chainweb/pact/local_test.exs index 159cc7e..c1b09cc 100644 --- a/test/chainweb/pact/local_test.exs +++ b/test/chainweb/pact/local_test.exs @@ -35,7 +35,9 @@ defmodule Kadena.Chainweb.Pact.LocalTest do @moduledoc """ `Local` endpoint implementation tests. """ + use ExUnit.Case + alias Kadena.Chainweb.Client.CannedLocalRequests alias Kadena.Chainweb.{Error, Pact} alias Kadena.Chainweb.Pact.LocalResponse @@ -50,59 +52,58 @@ defmodule Kadena.Chainweb.Pact.LocalTest do Application.delete_env(:kadena, :http_client_impl) end) - code = "(+ 5 6)" - bad_code = "(coin.get-balance 'bad')" - cmd = create_command(code) - cmd_for_error = create_command(bad_code) + success_cmd = create_command("(+ 5 6)") + error_cmd = create_command("(coin.get-balance 'bad')") + + success_response = + {:ok, + %LocalResponse{ + continuation: nil, + events: nil, + gas: 6, + logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", + meta_data: %{ + block_height: 3_303_861, + block_time: 1_671_546_034_831_940, + prev_block_hash: "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", + public_meta: %{ + chain_id: "1", + creation_time: 1_671_462_208, + gas_limit: 1000, + gas_price: 1.0e-6, + sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", + ttl: 28_800 + } + }, + req_key: "bTrbGGOdhzA_lwmMoXkqozNe_YKsww7uTNW913B79bs", + result: %{data: 11, status: "success"}, + tx_id: nil + }} + + error_response = + {:error, + %Error{ + status: 400, + title: "Validation failed: Invalid command: Failed reading: empty" + }} %{ - cmd: cmd, - cmd_for_error: cmd_for_error, - local_response: - {:ok, - %LocalResponse{ - continuation: nil, - events: nil, - gas: 6, - logs: "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", - meta_data: %{ - block_height: 3_303_861, - block_time: 1_671_546_034_831_940, - prev_block_hash: "Y6Wj0sxJpdV8M-3ihAbzUka57Wv5ZV2Uez6H_6_WeeY", - public_meta: %{ - chain_id: "1", - creation_time: 1_671_462_208, - gas_limit: 1000, - gas_price: 1.0e-6, - sender: "k:d1a361d721cf81dbc21f676e6897f7e7a336671c0d5d25f87c10933cac6d8cf7", - ttl: 28_800 - } - }, - req_key: "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", - result: %{data: 11, status: "success"}, - tx_id: nil - }} + success: %{cmd: success_cmd, response: success_response}, + error: %{cmd: error_cmd, response: error_response} } end - test "process/2", %{ - cmd: cmd, - local_response: local_response - } do - ^local_response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) + test "process/2", %{success: %{cmd: cmd, response: response}} do + ^response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) end - test "process/2 error", %{cmd_for_error: cmd_for_error} do - {:error, - %Error{ - status: 400, - title: "Validation failed: Invalid command: Failed reading: empty" - }} = Pact.local(cmd_for_error, network_id: :testnet04, chain_id: 1) + test "process/2 error", %{error: %{cmd: cmd, response: response}} do + ^response = Pact.local(cmd, network_id: :testnet04, chain_id: 1) end defp create_command(code) do network_id = :testnet04 - nonce = "2023-06-13 17:45:18.211131 UTC" + nonce = "2023-01-01 00:00:00.000000 UTC" {:ok, keypair} = Cryptography.KeyPair.from_secret_key( diff --git a/test/support/fixtures/chainweb/local.json b/test/support/fixtures/chainweb/local.json index f4903a4..d752a14 100644 --- a/test/support/fixtures/chainweb/local.json +++ b/test/support/fixtures/chainweb/local.json @@ -4,7 +4,7 @@ "status": "success", "data": 11 }, - "reqKey": "SvVzKL5KC0r1JdIkJDhMHT8vZhmO2CdTxPKX-6YmGG0", + "reqKey": "bTrbGGOdhzA_lwmMoXkqozNe_YKsww7uTNW913B79bs", "logs": "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8", "metaData": { "publicMeta": {