Skip to main content

Node queries

The node speaks plain JSON-RPC 2.0 over HTTP POST. No handshake, no encryption, and the credential for the owner surface is created for you.

Two things decide every call you write:

  • Which surface. /v2/owner is node administration. /v2/foreign is every chain read plus transaction submission. The split is listed in the node API.
  • The double envelope. A response has a JSON-RPC result, and inside it an Ok or Err. See the response envelope.

The owner credential is at ~/.epic/main/.api_secret (floo or user for other networks) and the username is the literal string epic. Foreign calls take no credential. See authentication.

Both clients honour NODE_URL and EPIC_NETWORK, so the local chain from run a local network needs no edit. Run them from the examples directory:

NODE_URL=http://127.0.0.1:23413 EPIC_NETWORK=user python python/epic_node.py

A complete client

Needs curl and jq.

examples/shell/node-status.sh
#!/usr/bin/env bash
# Chain status and tip from a local Epic node, over JSON-RPC.
# Needs curl and jq. See https://devdocs.epiccash.com/examples/node-api
set -euo pipefail

NODE_URL="${NODE_URL:-http://127.0.0.1:3413}"
EPIC_NETWORK="${EPIC_NETWORK:-main}"
SECRET_FILE="${SECRET_FILE:-$HOME/.epic/$EPIC_NETWORK/.api_secret}"

command -v jq >/dev/null || { echo "this script needs jq" >&2; exit 1; }

# jq exits non-zero on the Err branch, so a pipeline fails loudly instead of
# printing an error object as if it were data.
epic_ok() {
jq -e 'if .error then error("jsonrpc: \(.error)")
elif .result.Err then error("epic: \(.result.Err)")
else (.result.Ok // .result) end'
}

# Owner surface: needs the secret when the node has one. Status, peers, chain admin.
owner_rpc() {
local -a cred=()
[ -r "$SECRET_FILE" ] && cred=(-u "epic:$(cat "$SECRET_FILE")")
curl -s "${cred[@]}" \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$1\",\"params\":${2:-[]}}" \
"$NODE_URL/v2/owner" | epic_ok
}

# Foreign surface: no credential. Every chain read lives here.
foreign_rpc() {
curl -s \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$1\",\"params\":${2:-[]}}" \
"$NODE_URL/v2/foreign" | epic_ok
}

status=$(owner_rpc get_status)
echo "height: $(jq -r '.tip.height' <<<"$status")"
echo "sync state: $(jq -r '.sync_status' <<<"$status")"
echo "peers: $(jq -r '.connections' <<<"$status")"

echo "mempool: $(foreign_rpc get_pool_size)"
echo "stempool: $(foreign_rpc get_stempool_size)"
echo "version: $(foreign_rpc get_version | jq -r '.node_version')"

Chain status

get_status on the owner surface returns height, total difficulty, connected peers and sync state.

curl -s -u "epic:$(cat ~/.epic/main/.api_secret)" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"get_status","params":[],"id":1}' \
http://127.0.0.1:3413/v2/owner | jq '.result.Ok'

On Windows PowerShell:

$secret = (Get-Content "$HOME\.epic\main\.api_secret" -Raw).Trim()
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("epic:$secret"))
$body = '{"jsonrpc":"2.0","method":"get_status","params":[],"id":1}'
Invoke-RestMethod -Uri 'http://127.0.0.1:3413/v2/owner' -Method Post -Body $body `
-ContentType 'application/json' -Headers @{ Authorization = "Basic $auth" }
total_difficulty is an object, not a number

Epic runs several proof-of-work algorithms, so difficulty is tracked per algorithm. See proof of work.

{"total_difficulty": {"cuckaroo": 0, "cuckatoo": 1234, "randomx": 5678, "progpow": 9012}}

Fetch a block

get_block is on the foreign surface and takes positional params, [height, hash, commit]. Pass one and leave the others null.

curl -s -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"get_block","params":[1000000,null,null],"id":1}' \
http://127.0.0.1:3413/v2/foreign | jq '.result.Ok.header | {height, timestamp, proof}'

Kernels appear in two shapes depending on the method. get_block returns a flattened form, while get_kernel and get_last_n_kernels return the raw type with an externally tagged features field.

Watch for new blocks

The node API has no subscription mechanism, so polling is the approach. At a 60-second target block time, every 10 to 15 seconds is ample. Poll get_tip on the foreign surface, which needs no credential.

examples/shell/watch-blocks.sh
#!/usr/bin/env bash
# Poll the tip and print each new block height. Needs curl and jq.
# The node also has a webhook mechanism if you would rather be pushed to;
# see [server.webhook_config] in epic-server.toml.
set -euo pipefail

NODE_URL="${NODE_URL:-http://127.0.0.1:3413}"
INTERVAL="${INTERVAL:-10}"

command -v jq >/dev/null || { echo "this script needs jq" >&2; exit 1; }

height() {
# tr -d '\r' because a Windows jq writes CRLF, which command substitution strips only the
# newline from. Without it the integer comparison below fails with "integer expression
# expected" and the loop never advances. Harmless on Linux and macOS.
curl -s -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"get_tip","params":[]}' \
"$NODE_URL/v2/foreign" | jq -e -r '.result.Ok.height' | tr -d '\r'
}

last=$(height)
echo "starting at height $last"

while true; do
sleep "$INTERVAL"
now=$(height)
while [ "$last" -lt "$now" ]; do
last=$((last + 1))
echo "new block: $last"
done
done

For push rather than poll, see [server.webhook_config] in epic-server.toml, which can POST on transaction, header and block events.

Mempool and peers

Mempool size and version are foreign-surface calls. Peer lists are owner-surface calls.

rpc() { curl -s -H 'Content-Type: application/json' -d "$1" http://127.0.0.1:3413/v2/foreign; }

rpc '{"jsonrpc":"2.0","method":"get_pool_size","params":[],"id":1}' | jq '.result.Ok'
rpc '{"jsonrpc":"2.0","method":"get_stempool_size","params":[],"id":1}' | jq '.result.Ok'

curl -s -u "epic:$(cat ~/.epic/main/.api_secret)" -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"get_connected_peers","params":[],"id":1}' \
http://127.0.0.1:3413/v2/owner | jq '.result.Ok | length'

Every other method

Full JSON-RPC method list, by surface

/v2/owner, defined in api/src/owner_rpc.rs:29:

get_status validate_chain compact_chain
get_peers get_connected_peers ban_peer
unban_peer get_onion_addresses

/v2/foreign, defined in api/src/foreign_rpc.rs:37:

get_header get_block get_blocks
get_tip get_version get_kernel
get_last_n_kernels get_outputs get_unspent_outputs
get_pmmr_indices get_pool_size get_stempool_size
get_unconfirmed_transactions push_transaction
get_block_template finalize_block_template submit_block

Response types are defined in api/src/types.rs.

Next