Skip to content

Commit

Permalink
Local endpoint implementation (kommitters#177)
Browse files Browse the repository at this point in the history
* Add local endpoint implementation

Co-authored-by: Miguel Nieto A <[email protected]>
  • Loading branch information
EdwinGuayacan and miguelnietoa committed Dec 22, 2022
1 parent e490f49 commit 0175b67
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 75 deletions.
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lib/chainweb/pact.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ defmodule Kadena.Chainweb.Pact do
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
defdelegate local(cmds, network_opts \\ @default_network_opts), to: Local, as: :process
end
38 changes: 38 additions & 0 deletions lib/chainweb/pact/local.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule Kadena.Chainweb.Pact.Local do
@moduledoc """
Local endpoint implementation
"""

alias Kadena.Chainweb.Request
alias Kadena.Chainweb.Pact.{LocalRequestBody, LocalResponse, Spec}
alias Kadena.Types.Command

@behaviour Spec

@endpoint "local"

@type cmd :: Command.t()
@type json :: String.t()

@impl true
def process(%Command{} = cmd, network_id: network_id, chain_id: chain_id) do
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(body)
|> Request.add_headers(headers)
|> Request.perform()
|> Request.results(as: LocalResponse)
end

@spec json_request_body(cmd :: cmd()) :: json()
defp json_request_body(cmd) do
cmd
|> LocalRequestBody.new()
|> LocalRequestBody.to_json!()
end
end
16 changes: 4 additions & 12 deletions lib/chainweb/pact/local_request_body.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lib/chainweb/pact/spec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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()]
Expand Down
10 changes: 1 addition & 9 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -119,31 +120,24 @@ 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,
Kadena.Types.PactPayload,
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,
Expand All @@ -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": [
Expand Down
79 changes: 42 additions & 37 deletions test/chainweb/pact/local_request_body_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,67 @@ defmodule Kadena.Chainweb.Pact.LocalRequestBodyTest do

alias Kadena.Chainweb.Pact.LocalRequestBody

alias Kadena.Types.{PactTransactionHash, SignaturesList}
alias Kadena.Types.{
Command,
PactTransactionHash,
Signature,
SignaturesList
}

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,
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

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
%{
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
^json_result =
[hash: hash, sigs: sigs, cmd: cmd]
command
|> LocalRequestBody.new()
|> LocalRequestBody.to_json!()
end
Expand Down
Loading

0 comments on commit 0175b67

Please sign in to comment.