Current section
Files
Jump to
Current section
Files
lib/rpc/rpc.ex
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=<tx>
"""
@spec send(binary()) :: any()
def send(tx) do
url = "http://#{rpc_host()}"
body = %{
"method" => "broadcast_tx_sync",
"jsonrpc" => "2.0",
"params" => [Base.encode64(tx)],
"id" => "1"
}
post_url(url, body)
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 post_url(url, data) do
case HTTPoison.post(url, Jason.encode!(data)) do
{:ok, %HTTPoison.Response{body: body}} ->
case Jason.decode!(body) do
%{"result" => result} ->
result
%{"error" => error} ->
Logger.error("Failed to call tendermint RPC. Error: #{inspect(error)}")
nil
end
{: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