From 59a67d08e5e05901a4dee5e757e2082eb6636cf7 Mon Sep 17 00:00:00 2001 From: victor felder Date: Thu, 2 May 2024 17:35:44 +0200 Subject: [PATCH] feat: send operations to RPC node --- lib/application.ex | 17 +++++ lib/crypto.ex | 10 +-- lib/rpc.ex | 166 +++++++++++++++++++++++++++++++++++++++++++ lib/transaction.ex | 53 ++++++++++++++ mix.exs | 5 +- mix.lock | 8 +++ test/crypto_test.exs | 10 +-- test/rpc_test.exs | 99 ++++++++++++++++++++++++++ 8 files changed, 357 insertions(+), 11 deletions(-) create mode 100644 lib/application.ex create mode 100644 lib/rpc.ex create mode 100644 lib/transaction.ex create mode 100644 test/rpc_test.exs diff --git a/lib/application.ex b/lib/application.ex new file mode 100644 index 0000000..eaf7454 --- /dev/null +++ b/lib/application.ex @@ -0,0 +1,17 @@ +defmodule Tezex.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + @impl true + def start(_type, _args) do + children = [{Finch, name: Tezex.Finch}] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Tezex.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lib/crypto.ex b/lib/crypto.ex index ee54d0e..b25f7ee 100644 --- a/lib/crypto.ex +++ b/lib/crypto.ex @@ -254,11 +254,11 @@ defmodule Tezex.Crypto do ## Examples iex> encoded_private_key = "spsk24EJohZHJkZnWEzj3w9wE7BFARpFmq5WAo9oTtqjdJ2t4pyoB3" iex> Tezex.Crypto.sign_message(encoded_private_key, "foo") - "sigm9uJiGjdk2DpuqTmHcjzpAdTSQfqKxFuDKodyNT8JP3UvrfoPFTNkFbFgDP1WfAi2PjJ3dcpZFLTagD7gUBmwVWbPr5mk" + "spsig1Uyadmsz75zND5qDjSAteir1NGCEuPaxNnT8QmXkwCJkuzWUyxKqKsjSx3nU4uj2nk8t31VFFhQ4YsPKdZ9ghA2d2fe9HF" iex> msg = Tezex.Micheline.pack("foo", :string) "050100000003666f6f" iex> Tezex.Crypto.sign_message(encoded_private_key, msg) - "sigm9uJiGjdk2DpuqTmHcjzpAdTSQfqKxFuDKodyNT8JP3UvrfoPFTNkFbFgDP1WfAi2PjJ3dcpZFLTagD7gUBmwVWbPr5mk" + "spsig1Uyadmsz75zND5qDjSAteir1NGCEuPaxNnT8QmXkwCJkuzWUyxKqKsjSx3nU4uj2nk8t31VFFhQ4YsPKdZ9ghA2d2fe9HF" """ @spec sign_message(privkey_param(), binary()) :: nonempty_binary() def sign_message(privkey_param, "0501" <> _ = bytes) do @@ -279,7 +279,7 @@ defmodule Tezex.Crypto do "ed" <> _ -> bytes_hash = Blake2.hash2b(watermark <> msg, 32) signature = :crypto.sign(:eddsa, :none, bytes_hash, [decoded_key, :ed25519]) - Base58Check.encode(signature, @prefix_sig) + Base58Check.encode(signature, @prefix_edsig) "sp" <> _ -> pk = %PrivateKey{secret: decoded_key, curve: KnownCurves.secp256k1()} @@ -293,7 +293,7 @@ defmodule Tezex.Crypto do signature = :binary.decode_hex(r_bin <> s_bin) - Base58Check.encode(signature, @prefix_sig) + Base58Check.encode(signature, @prefix_spsig) "p2" <> _ -> pk = %PrivateKey{secret: decoded_key, curve: KnownCurves.prime256v1()} @@ -307,7 +307,7 @@ defmodule Tezex.Crypto do signature = :binary.decode_hex(r_bin <> s_bin) - Base58Check.encode(signature, @prefix_sig) + Base58Check.encode(signature, @prefix_p2sig) end end diff --git a/lib/rpc.ex b/lib/rpc.ex new file mode 100644 index 0000000..97d8f43 --- /dev/null +++ b/lib/rpc.ex @@ -0,0 +1,166 @@ +defmodule Tezex.Rpc do + alias Tezex.Crypto + alias Tezex.Crypto.Base58Check + alias Tezex.ForgeOperation + alias Tezex.Rpc + alias Tezex.Transaction + + @type t() :: %__MODULE__{ + endpoint: binary(), + chain_id: binary(), + headers: Finch.Request.headers(), + opts: Finch.request_opts() + } + + defstruct [:endpoint, chain_id: "main", headers: [], opts: []] + + # Various constants for the Tezos platforms. Fees are expressed in µtz unless otherwise noted, storage unit is bytes. + @operation_group_watermark <<3>> + + @default_simple_transaction_fee 1420 + @default_transaction_storage_limit 496 + @default_transaction_gas_limit 10600 + + @default_delegation_fee 1258 + @default_delegation_storage_limit 0 + @default_delegation_gas_limit 1100 + + @default_key_reveal_fee 1270 + @default_key_reveal_storage_limit 0 + @default_key_reveal_gas_limit 1100 + + @p005_manager_contract_withdrawal_gas_limit 26283 + @p005_manager_contract_deposit_gas_limit 15285 + @p005_manager_contract_withdrawal_storage_limit 496 + + @p001_storage_rate 1_000_000 / 1000 + @p007_storage_rate 250_000 / 1000 + @storage_rate @p007_storage_rate + + @base_operation_fee 100 + + @p009_block_gas_cap 10_400_000 + @p010_block_gas_cap 5_200_000 + @p016_block_gas_cap 2_600_000 + @p007_operation_gas_cap 1_040_000 + @operation_gas_cap @p007_operation_gas_cap + @block_gas_cap @p016_block_gas_cap + + @p010_operation_storage_cap 60_000 + @p011_operation_storage_cap 32_768 + @operation_storage_cap @p011_operation_storage_cap + + @empty_account_storage_burn 257 + + @default_baker_vig 200 + + @gas_limit_padding 1000 + @storage_limit_padding 25 + + @head_branch_offset 54 + @max_branch_offset 64 + + # Outbound operation queue timeout in seconds. After this period, TezosOperationQueue will attempt to submit the transactions currently in queue. + @default_batch_delay 25 + + @p009_block_time 60 + @p010_block_time 30 + @default_block_time @p010_block_time + + @genesis_block_time ~U[2018-06-30 10:07:32.000Z] + + def build_contract_operation( + %Rpc{} = rpc, + public_key_hash, + counter, + contract, + amount, + fee, + storage_limit, + gas_limit, + entrypoint, + parameters, + encoded_private_key + ) do + parameters = + cond do + not is_nil(parameters) -> %{entrypoint: entrypoint || "default", value: parameters} + not is_nil(entrypoint) -> %{entrypoint: entrypoint, value: []} + true -> nil + end + + %Transaction{ + source: public_key_hash, + destination: contract, + amount: Integer.to_string(amount), + storage_limit: Integer.to_string(storage_limit), + gas_limit: Integer.to_string(gas_limit), + counter: Integer.to_string(counter), + fee: Integer.to_string(fee), + kind: "transaction", + parameters: parameters + } + end + + def send_operation(%Rpc{} = rpc, operations, encoded_private_key, offset) do + {:ok, block_head} = get_block_at_offset(rpc, offset) + block_hash = binary_part(block_head["hash"], 0, 51) + + forged_operation_group = + ForgeOperation.forge_operation_group(%{"branch" => block_hash, "contents" => operations}) + + op_signature = + Crypto.sign_message( + encoded_private_key, + @operation_group_watermark <> forged_operation_group + ) + + signed_op_group = forged_operation_group <> op_signature + + op_pair = %{bytes: signed_op_group, signature: op_signature} + + applied = preapply_operation(rpc, block_hash, block_head["protocol"], operations, op_pair) + injected_op = inject_operation(rpc, op_pair) + + %{results: applied[0], operation_group_id: injected_op = inject_operation(rpc, op_pair)} + end + + def get_counter_for_account(%Rpc{} = rpc, address) do + with {:ok, n} <- get(rpc, "/blocks/head/context/contracts/#{address}/counter"), + {n, ""} <- Integer.parse(n) do + n + end + end + + def get_next_counter_for_account(%Rpc{} = rpc, address) do + get_counter_for_account(rpc, address) + 1 + end + + def get_block(%Rpc{} = rpc, hash \\ "head") do + get(rpc, "/blocks/#{hash}") + end + + def get_block_at_offset(%Rpc{} = rpc, offset) do + if offset <= 0 do + get_block(rpc) + end + + {:ok, head} = get_block(rpc) + get(rpc, "/blocks/#{head["header"]["level"] - offset}") + end + + def get(%Rpc{} = rpc, path) do + url = + URI.parse(rpc.endpoint) + |> URI.append_path("/chains/#{rpc.chain_id}") + |> URI.append_path(path) + |> URI.to_string() + + Finch.build(:get, url, rpc.headers) + |> Finch.request(Tezex.Finch, rpc.opts) + |> case do + {:ok, %Finch.Response{status: 200, body: body}} -> Jason.decode(body) + {:error, _} = err -> err + end + end +end diff --git a/lib/transaction.ex b/lib/transaction.ex new file mode 100644 index 0000000..8303f42 --- /dev/null +++ b/lib/transaction.ex @@ -0,0 +1,53 @@ +defmodule Tezex.Transaction do + alias Tezex.Transaction + + @type t() :: %__MODULE__{ + source: binary() | nil, + destination: binary(), + fee: pos_integer() | nil, + counter: integer() | nil, + gas_limit: pos_integer() | nil, + amount: pos_integer() | nil, + destination: binary(), + storage_limit: pos_integer() | nil, + parameters: map() | nil + } + + defstruct [ + :kind, + :destination, + :source, + fee: 0, + counter: 0, + gas_limit: 0, + amount: 0, + storage_limit: 0, + parameters: nil + ] + + defimpl Jason.Encoder do + def encode(%Transaction{parameters: nil} = transaction, opts) do + transaction + |> Map.from_struct() + |> Map.drop([:parameters]) + |> encode(opts) + end + + def encode(%Transaction{} = transaction, opts) do + transaction + |> Map.from_struct() + |> encode(opts) + end + + def encode(value, opts) do + value + |> Map.put(:kind, "transaction") + |> Map.update!(:fee, &to_string/1) + |> Map.update!(:gas_limit, &to_string/1) + |> Map.update!(:amount, &to_string/1) + |> Map.update!(:storage_limit, &to_string/1) + |> Map.update!(:counter, &to_string/1) + |> Jason.Encode.map(opts) + end + end +end diff --git a/mix.exs b/mix.exs index cadbfcf..c55ed36 100644 --- a/mix.exs +++ b/mix.exs @@ -13,6 +13,7 @@ defmodule Tezex.MixProject do description: description(), package: package(), deps: deps(), + start_permanent: Mix.env() == :prod, test_coverage: [tool: ExCoveralls], preferred_cli_env: [ coveralls: :test, @@ -55,6 +56,7 @@ defmodule Tezex.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ + mod: {Tezex.Application, []}, extra_applications: [:logger, :crypto] ] end @@ -68,7 +70,8 @@ defmodule Tezex.MixProject do {:ex_doc, "~> 0.27", only: :dev, runtime: false}, {:ex_unit_notifier, "~> 1.2", only: :test}, {:excoveralls, "~> 0.15.1", only: :test}, - {:jason, "~> 1.4", only: :test}, + {:finch, "~> 0.10"}, + {:jason, "~> 1.4"}, {:mix_test_watch, "~> 1.0", only: [:dev, :test], runtime: false}, {:ssl_verify_fun, "~> 1.1", manager: :rebar3, override: true} ] diff --git a/mix.lock b/mix.lock index 58a3a26..8f42c69 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,7 @@ %{ "base_58_check": {:hex, :base_58_check, "1.0.0", "bbe527bb00ffd9c5b03abdf970aefd9ca36bd3967dcd75efa8a3c2ed1dcec154", [:mix], [], "hexpm", "06b538938722a04680210e889edaf96f4c967251218fbfbafa19ec2702ba489c"}, "blake2": {:hex, :blake2, "1.0.4", "8263c69a191142922bc2510f1ffc0de0ae96e8c3bd5e2ad3fac7e87aed94c8b1", [:mix], [], "hexpm", "e9f4120d163ba14d86304195e50745fa18483e6ad2be94c864ae449bbdd6a189"}, + "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, @@ -9,17 +10,24 @@ "ex_unit_notifier": {:hex, :ex_unit_notifier, "1.3.0", "1d82aa6d2fb44e6f0f219142661a46e13dcba833e150e1395190d2e0fb721990", [:mix], [], "hexpm", "55fffd6062e8d962fc44e8b06fa30a87dc7251ee2a69f520781a3bb29858c365"}, "excoveralls": {:hex, :excoveralls, "0.15.3", "54bb54043e1cf5fe431eb3db36b25e8fd62cf3976666bafe491e3fa5e29eba47", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f8eb5d8134d84c327685f7bb8f1db4147f1363c3c9533928234e496e3070114e"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, + "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, + "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, "mix_test_watch": {:hex, :mix_test_watch, "1.2.0", "1f9acd9e1104f62f280e30fc2243ae5e6d8ddc2f7f4dc9bceb454b9a41c82b42", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "278dc955c20b3fb9a3168b5c2493c2e5cffad133548d307e0a50c7f2cfbf34f6"}, + "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } diff --git a/test/crypto_test.exs b/test/crypto_test.exs index 58ed080..6589091 100644 --- a/test/crypto_test.exs +++ b/test/crypto_test.exs @@ -239,7 +239,7 @@ defmodule Tezex.CryptoTest do watermark = <<3>> signature = - "sigeeho3jK4MZKsyqcTc9mjhtz9w6enG3AFniufgUXCuXFW7VjShxLoNmxqkQSRYUwP1LHRMere5LrvxcqLgU9KmDGN356Yz" + "edsigtpUAv6xDLHkMwApubdzxh1BVvmBaktukdUUeQ2UWDFYCowSKwbsRtS1stM2EX1fHpYouLsSDUhJdfkf2zpp7WpHNGqphui" assert Crypto.derive_address(pubkey) == {:ok, pubkeyhash} assert signature == Crypto.sign(encoded_private_key, bytes, watermark) @@ -259,7 +259,7 @@ defmodule Tezex.CryptoTest do watermark = <<3>> signature = - "sigpKAnfQGzG4Rk5pV7z9mx2TL9veQCHD7qN4PhsUZMj1BqsumBoApBS9Ue616vKVymxrzfZE2L4h27zzxRUVy6BNPRMpufb" + "edsigtz8duidBGCFToGonG1zxuKjqvkjLAv5iD3p8SDUYN5299hrMgh5uGVPPggcu1ngLDWLcb51PAgenro3A5cqwHESVJ31m4x" assert Crypto.derive_address(pubkey) == {:ok, pubkeyhash} assert signature == Crypto.sign(encoded_private_key, bytes, watermark) @@ -278,7 +278,7 @@ defmodule Tezex.CryptoTest do watermark = <<3>> signature = - "sigREwM1SuRN5WzjH5xGJuyeZQ9kWi8XtbA4wRqGTumJwNY18PmF1XQMLCXEQBr4frnriKHWdPUynF1vGUvPcoWrNjb3s5xp" + "spsig194cg549ti3fNuGqvQs4dZEn4aJHCbXHde4kc4dDaCs5y4nCwp5uMUp4DbyGuTjisaTU2UV1v7vy7CybSGJJS4ur88uBWT" assert Crypto.derive_address(pubkey) == {:ok, pubkeyhash} assert signature == Crypto.sign(encoded_private_key, bytes, watermark) @@ -295,7 +295,7 @@ defmodule Tezex.CryptoTest do watermark = <<3>> signature = - "sigmVKa3AcvzDTPGD7rJXkrMh8XMVkVQUkLwGLL3h1APWgicRKBmgjZ3624vqHA2FufBrLTQuPS9YBN1h2Z16kexp9F8NRXp" + "spsig1VJzu6msQLBbmSCspT5uWGNWSBHKZU7SpWPf6qrJyHSQ9g58NLm7WAZsmJQNDRKmjubVCNm1sHguTJQ94snFaBMFkDWskF" assert Crypto.derive_address(pubkey) == {:ok, pubkeyhash} assert signature == Crypto.sign(encoded_private_key, bytes, watermark) @@ -312,7 +312,7 @@ defmodule Tezex.CryptoTest do watermark = <<3>> signature = - "sigRUobRZ4oG7CvdtQfNoDe3Gqn8zrLw6RXjXVa84rX3HMzWuCuncWgDqY8wRkssofLUsfbdR6MNGJzDWaNXEwTxDktfrKmj" + "p2sigQo2SWectj3FPa9Av6PkRYjdAXf5c2WqkBJLxXXHpFjeY4AzEAfbuW1xS8NH7K1Ef6uBchf2gsDdMcCW77h8uDv6tdZ2ZS" assert Crypto.derive_address(pubkey) == {:ok, pubkeyhash} assert signature == Crypto.sign(encoded_private_key, bytes, watermark) diff --git a/test/rpc_test.exs b/test/rpc_test.exs new file mode 100644 index 0000000..9cb8fa6 --- /dev/null +++ b/test/rpc_test.exs @@ -0,0 +1,99 @@ +defmodule Tezex.RpcTest do + use ExUnit.Case, async: true + + alias Tezex.Rpc + doctest Tezex.Rpc + + @endpoint "https://ghostnet.tezos.marigold.dev/" + + test "get_counter_for_account" do + counter = + Rpc.get_counter_for_account( + %Rpc{endpoint: @endpoint}, + "tz1LKpeN8ZSSFNyTWiBNaE4u4sjaq7J1Vz2z" + ) + + assert is_integer(counter) + end + + test "build_contract_operation" do + rpc = %Rpc{endpoint: @endpoint} + public_key_hash = "tz1b9kV41KV9N3sp69ycLdSoZ2Ak8jXwtNPv" + encoded_private_key = "edsk4TjJWEszkHKono7XMnepVqwi37FrpbVt1KCsifJeAGimxheShG" + counter = Rpc.get_next_counter_for_account(rpc, public_key_hash) + + contract = "KT1MFWsAXGUZ4gFkQnjByWjrrVtuQi4Tya8G" + entrypoint = "offer" + gas_limit = 1000 + storage_limit = 1000 + fee = 1000 + amount = 1 + + price = 100 + fa_contract = "KT1JzqDsR2eMqhu76uRqAUEuTWj7bbwqa9wY" + token_id = "5" + royalty_address = public_key_hash + royalty_share = 1000 + + parameters = %{ + prim: "Pair", + args: [ + %{ + prim: "Pair", + args: [%{string: fa_contract}, %{prim: "Some", args: [%{int: token_id}]}] + }, + %{ + prim: "Pair", + args: [ + %{prim: "Right", args: [%{prim: "Right", args: [%{prim: "Unit"}]}]}, + %{ + prim: "Pair", + args: [ + %{int: Integer.to_string(price)}, + %{ + prim: "Pair", + args: [ + [ + %{ + prim: "Elt", + args: [ + %{string: royalty_address}, + %{int: Integer.to_string(royalty_share)} + ] + } + ], + %{ + prim: "Pair", + args: [ + %{prim: "None"}, + %{ + prim: "Pair", + args: [[], %{prim: "Pair", args: [%{prim: "None"}, %{prim: "None"}]}] + } + ] + } + ] + } + ] + } + ] + } + ] + } + + tx = + Rpc.build_contract_operation( + rpc, + public_key_hash, + counter, + contract, + amount, + fee, + storage_limit, + gas_limit, + entrypoint, + parameters, + encoded_private_key + ) + end +end