defmodule Forge.Rpc do @moduledoc """ RPC lib to send tx. Using localhost:26657 for simplicity. """ require Logger alias Forge.{Helper, TransactionMessage} @doc """ Send out the signed transaction with tendermint http RPC interface: localhost:26657/broadcast_tx_commit?tx= """ @spec send(binary()) :: any() def send(tx) do url = "http://#{rpc_host()}/broadcast_tx_commit?tx=\"#{Base.url_encode64(tx, padding: false)}\"" get_url(url) end def get_tx(hash) do url = "http://#{rpc_host()}/tx?hash=0x#{hash}" get_url(url) end def query_tx(key, value) do url = "http://#{rpc_host()}/tx_search?query=\"#{key}='#{value}'\"" get_url(url) end def get_status do url = "http://#{rpc_host()}/status" %{ "node_info" => node, "sync_info" => sync, "validator_info" => validator } = get_url(url) %{ id: node["id"], network: node["network"], moniker: node["moniker"], version: node["version"], synced: not sync["catching_up"], app_hash: sync["latest_app_hash"], block_hash: sync["latest_block_hash"], block_height: String.to_integer(sync["latest_block_height"]), block_time: sync["latest_block_time"], validator: validator["address"], voting_power: String.to_integer(validator["voting_power"]) } end @spec get_block(non_neg_integer()) :: %{ app_hash: String.t(), height: non_neg_integer(), num_txs: non_neg_integer(), proposer: String.t(), time: DateTime.t(), txs: list(TransactionMessage.t()) } def get_block(height) do url = "http://#{rpc_host()}/block?height=#{height}" %{"block" => %{"header" => header, "data" => data}} = get_url(url) {:ok, dt, _} = DateTime.from_iso8601(header["time"]) %{ height: String.to_integer(header["height"]), num_txs: String.to_integer(header["num_txs"]), time: dt, app_hash: header["app_hash"], proposer: header["proposer_address"], txs: parse_tx(data["txs"]) } end # private function defp get_url(url) do case HTTPoison.get(url) do {:ok, %HTTPoison.Response{body: body}} -> %{"result" => result} = Jason.decode!(body) result {:error, error} -> Logger.error("Failed to call tendermint RPC. Error: #{inspect(error)}") nil end end defp rpc_host do Application.get_env(:forge, :rpc_host, "localhost:26657") end defp parse_tx(nil), do: nil defp parse_tx(data) when is_list(data), do: Enum.map(data, &parse_tx/1) defp parse_tx(data), do: data |> Base.decode64!() |> Helper.decode_tx() end