Build
JSON-RPC API
Every method of a Ksana node’s JSON-RPC interface and of the indexer’s, with the JSON conventions, the errors, and real calls to the public signet.
On this page
- Endpoints
- Calls from web pages
- A first call
- JSON conventions
- Methods at a glance
- Chain and fees
ksana_getChainInfoksana_estimateFee- Accounts and assets
ksana_getAccountksana_getAsset- Operations
ksana_submitOpksana_getOpStatusksana_getAccountOpsksana_getLatestOps- Blocks and settlements
ksana_getBlockksana_getSettlement- Read-only calls
ksana_call- Subscriptions
ksana_subscribeBlocks- Indexer methods
indexer_statusindexer_stateRoot- Errors
A Ksana node answers JSON-RPC 2.0 over HTTP and WebSocket on one port. The indexer answers two methods of its own. Wallets, apps, ksana-cli and the TypeScript SDK all use this interface.
Every example on this page is a real call to the public signet, made on 14 October 2026. Mainnet has not launched.
Endpoints
| Service | Default address | Public signet |
|---|---|---|
| Node, HTTP | 127.0.0.1:8545 (ksana-node --rpc-addr) |
https://signet.ksana.network/rpc |
| Node, WebSocket | the same port | wss://signet.ksana.network/rpc |
| Indexer, HTTP | 127.0.0.1:8546 (ksana-indexer --rpc-addr) |
https://signet.ksana.network/indexer |
Calls take positional parameters: params is a JSON array, in the order the sections below list them. Send content-type: application/json.
Calls from web pages
Browsers call another origin only when the server allows it (CORS). A node allows none by default; start it with --rpc-cors '*' to allow every origin, or with a comma-separated list of origins. Command-line tools and servers are not affected.
On the public signet, the node’s RPC allows every origin and the indexer allows none. A preflight request shows the difference:
curl -sS -i -X OPTIONS https://signet.ksana.network/rpc -H 'origin: https://example.org' \
-H 'access-control-request-method: POST' -H 'access-control-request-headers: content-type' \
| grep -i -E '^(HTTP/2|access-control)'HTTP/2 200
access-control-allow-headers: content-type
access-control-allow-methods: POST,GET,OPTIONS
access-control-allow-origin: *The same request to https://signet.ksana.network/indexer gets HTTP/2 405. Call the indexer from a server or a command line.
A first call
Ask the node for its chain tip:
curl -sS https://signet.ksana.network/rpc -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"ksana_getChainInfo","params":[]}' | jq{
"jsonrpc": "2.0",
"id": 1,
"result": {
"chain_id": 1002,
"tip": {
"height": "286",
"hash": "2c1363cf56e91f58e8bd2167b74625120771ad3b0956ea215de540faa33df3ad"
},
"daa_score": "12452",
"protocol_version": 2,
"next_upgrade": null,
"max_known_version": 2,
"finalized": {
"height": "246",
"hash": "c45e4b58173b529c164e66c61219befe4bf7b3e93c319f9c80dbd98982793071"
}
}
}The other examples use a small shell function and print only result. It needs curl and jq:
NODE=https://signet.ksana.network/rpc
INDEXER=https://signet.ksana.network/indexer
rpc() { # rpc <url> <method> [params as a JSON array]
curl -sS "$1" -H 'content-type: application/json' \
--data "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$2\",\"params\":${3:-[]}}"
}
ACCOUNT=d08a82dc475034a5f18bb589be8343472e67996b830764889bcbed12e24510bf
KSA=0000000000000000000000000000000000000000000000000000000000000000ACCOUNT is the account of the token and pool tutorial, and KSA is the asset ID of KSA.
JSON conventions
- Bytes (hashes, accounts, asset and contract IDs, Borsh encodings) are lowercase hex strings without
0x. u64andu128values (heights, nonces, amounts, gas) are decimal strings, because JavaScript numbers hold only 53 bits.u8andu32values (decimals, BTC heights, counts) are JSON numbers.- Block identifiers are
"latest", a decimal height such as"268", or a 64-digit block hash. A height names a block of the main chain; a hash can name any block the node knows. - Bitcoin IDs (
txid, BTC block hashes) are in Bitcoin’s internal byte order, the reverse of what explorers show. Fields that end in_rpcgive the explorer order. - Protocol types (block headers, entries, VmOps) use the JSON form of the repository’s test vectors: an enum is an object with one key, its variant, such as
{"Token": {"amount": "1"}}.
Methods at a glance
| Method | Parameters | Result |
|---|---|---|
ksana_getChainInfo |
none | chain ID, tip, DAA score, versions, finalized tip |
ksana_estimateFee |
none | suggested path B fee and the node’s BTC fee rate |
ksana_getAccount |
account | nonce, balances, locked fees |
ksana_getAsset |
asset ID | name, decimals, total supply, issuer |
ksana_submitOp |
VmOp (Borsh, hex) | op ID |
ksana_getOpStatus |
op ID | status, block, receipt |
ksana_getAccountOps |
account, before, limit |
a page of an account’s ops |
ksana_getLatestOps |
before, limit |
a page of the chain’s ops |
ksana_getBlock |
block identifier | header, entries, receipts |
ksana_getSettlement |
block identifier | the settlement and its Bitcoin status |
ksana_call |
call request | receipt of a read-only call |
ksana_subscribeBlocks |
none (WebSocket) | a notification per new block |
indexer_status |
none | the indexer’s verified tip |
indexer_stateRoot |
block identifier | a verified block’s state root |
Chain and fees
ksana_getChainInfo
No parameters. The result, shown in A first call:
| Field | Meaning |
|---|---|
chain_id |
1002 on the signet, 1001 on devnet, 1000 on mainnet |
tip |
the main chain’s tip, { "height", "hash" } |
daa_score |
the tip’s DAA score; each block adds its weight (an empty block weighs more) |
protocol_version |
the tip’s block header version |
next_upgrade |
the next scheduled upgrade, { "version", "daa_score" }, or null |
max_known_version |
the highest protocol version this node’s software supports |
finalized |
the finalized tip: the main chain must pass through it, and only a Bitcoin reorganization can take it back |
When the network’s protocol_version passes max_known_version, the node’s software needs an upgrade. Before any block is final, finalized is the genesis block.
ksana_estimateFee
No parameters. It suggests fees; it does not change what the node accepts.
rpc $NODE ksana_estimateFee | jq .result{
"feerate_sat_vb": "2",
"path_b": {
"amount": "1",
"per_kib": "1"
},
"at": {
"height": "286",
"hash": "2c1363cf56e91f58e8bd2167b74625120771ad3b0956ea215de540faa33df3ad"
}
}feerate_sat_vbis the fee rate, in sat/vB, at which this node pays for its settlements now. Use it as a reference for path C transactions and top-ups.path_b.per_kibis a suggested fee rate for path B: base units of KSA per KiB of cost. The cost of an op is the larger of its encoded size and itsmax_gasconverted to bytes (max_gas × 81,920 / 100,000,000). The node takes the median, over its last 20 main-chain blocks, of each block’s lowest path B fee rate, and never goes below its own--min-fee-per-kib.path_b.amountis the suggested fee of an ordinary KSA transfer at that rate, never below the node’s--min-token-fee. For another op, multiply its cost byper_kib.atis the tip the estimate was read at.
Operations and fees explains the fee rules.
Accounts and assets
ksana_getAccount
Parameter: the account, as 64 hex digits (the Taproot output key, not the tb1p address).
rpc $NODE ksana_getAccount "[\"$ACCOUNT\"]" | jq .result{
"account": "d08a82dc475034a5f18bb589be8343472e67996b830764889bcbed12e24510bf",
"nonce": "10",
"balances": [
{
"asset": "0000000000000000000000000000000000000000000000000000000000000000",
"amount": "7475669406"
}
],
"locked_fees": [],
"at": {
"height": "286",
"hash": "2c1363cf56e91f58e8bd2167b74625120771ad3b0956ea215de540faa33df3ad"
}
}nonceis the account’s next nonce on the main chain. Ops still waiting in the entry pool are not counted.balanceslists the available balance of each built-in asset, in base units. A token kept in a contract’s storage, such as thetokenexample, does not appear here: ask the contract.locked_feeslists what the account is owed by blocks that are not unlocked yet, one entry per block,[{ "block_hash", "amount" }]: its reward and path B fees as the block’s producer, its part as the block’s carrier, and the block share for the block share account. It is empty for an account owed nothing.atis the block the answer was read at.
ksana_getAsset
Parameter: the asset ID. KSA’s ID is 64 zeros.
rpc $NODE ksana_getAsset "[\"$KSA\"]" | jq .result{
"asset": "0000000000000000000000000000000000000000000000000000000000000000",
"name": "KSA",
"decimals": 8,
"total_supply": "11011956230000000",
"issuer": "0000000000000000000000000000000000000000000000000000000000000000",
"at": {
"height": "286",
"hash": "2c1363cf56e91f58e8bd2167b74625120771ad3b0956ea215de540faa33df3ad"
}
}total_supply is in base units: the genesis allocation plus the block rewards issued so far, minus burns. issuer is the contract that issued the asset, or zeros for KSA. An unknown asset returns -32004.
Operations
ksana_submitOp
Parameter: a signed path B VmOp, Borsh-encoded, in hex. Result: { "op_id" }.
The node checks the op against the consensus rules (version, chain ID, size, signature, a nonce not below the account’s) and against its own policy (minimum fee, capacity). A producing node answers once the op is in its next block template, or after 5 seconds at most.
- A path C op does not go through this method: it travels in a Bitcoin transaction, and
ksana_submitOprefuses it with-32010. - An op that breaks a consensus rule returns
-32010; an op the node’s policy refuses (fee too low, pool full, a replacement without a higher fee) returns-32011. - Bytes that do not decode return
-32602:
rpc $NODE ksana_submitOp '["00"]' | jq .error{
"code": -32602,
"message": "VmOp decode failed: Borsh decode failed: Unexpected length of input"
}The TypeScript SDK and ksana-cli build, sign and encode ops for you.
ksana_getOpStatus
Parameter: the op ID. The example asks for a faucet transfer:
OP=cdfbd418ad44c47c8fe5a31ba885e9bf1de6c13b5a467e79f4b2b735fc54be97
rpc $NODE ksana_getOpStatus "[\"$OP\"]" | jq .result{
"status": "fast_confirmed",
"block": {
"height": "261",
"hash": "81a22b58a74d0d6223cfb8c3c53a3ae21d13f0311c5d155f84863b5ba7be8639"
},
"index": 0,
"receipt": {
"op_id": "cdfbd418ad44c47c8fe5a31ba885e9bf1de6c13b5a467e79f4b2b735fc54be97",
"success": true,
"error": null,
"gas_used": "10000",
"output": "",
"events": []
}
}status |
Meaning |
|---|---|
pending |
in the entry pool, not yet in a block |
fast_confirmed |
in a block of the main chain and executed successfully |
failed |
in a block, but its execution failed: the state is rolled back and the fee is charged |
skipped |
included only by a merged block and could not be executed; no fee is charged, and the op can return to the entry pool |
btc_final |
executed successfully, and the block that accepted it is BTC-final |
block.hash is the block that included the op. block.height is the height of the main-chain block that accepted it: an op in a merged block takes effect at the height of the block that merges it. index is the entry’s place in its block. An unknown op returns -32004, and Confirmations explains the marks behind these statuses.
ksana_getAccountOps
Parameters: account, before (optional), limit (optional). It lists the ops of one account on the main chain, newest first: the ops it signed, the transfers to it, and the calls to it as a contract.
rpc $NODE ksana_getAccountOps "[\"$ACCOUNT\", null, 2]" | jq .result{
"ops": [
{
"op_id": "2f9117f181b5ea9b3269d91503bb564e77bd4eae54d8a6f48f4d6c77f1e1cc3d",
"status": "fast_confirmed",
"block": {
"height": "274",
"hash": "f337432055aaa71728d34ec078cc9b426654829790d8d39011e0ebd131b86a9a"
},
"index": 0,
"position": "274:0:0"
},
{
"op_id": "7e3c309278bbad5cc0f0c0dfcb55bbd3689cdced9d4410c704e2f09c89330026",
"status": "fast_confirmed",
"block": {
"height": "273",
"hash": "2b6f9218d05b361a38093e0425fdf4da8222fb0d8e93d8f8696ed8c4a192f60d"
},
"index": 0,
"position": "273:0:0"
}
],
"next": "273:0:0",
"at": {
"height": "286",
"hash": "2c1363cf56e91f58e8bd2167b74625120771ad3b0956ea215de540faa33df3ad"
}
}positionis an op’s place on the main chain,"height:slot:entry": the height of the main-chain block that accepted it, the place of its block among the blocks that one accepted, and the entry’s index. Listings run from the highest position down.beforeis a position: the page lists the ops below it. Pass the previous page’snextto continue, andnull(or nothing) for the newest ops.limitis 1 to 500, 50 by default.nextisnullonce the listing reaches the genesis block. A page can hold fewer ops thanlimit, even none, whilenextis notnull: keep followingnext.
The next page continues below 273:0:0:
rpc $NODE ksana_getAccountOps "[\"$ACCOUNT\", \"273:0:0\", 2]" \
| jq -c '.result.ops[].position, .result.next'"272:0:0"
"271:0:0"
"271:0:0"Listings include path B and path C ops, not unlock entries. A node from before these methods existed, or one whose op index is not built, returns -32601. A malformed account or position, or a limit out of range, returns -32602.
ksana_getLatestOps
Parameters: before (optional), limit (optional). The same listing as ksana_getAccountOps, for every account:
rpc $NODE ksana_getLatestOps '[null, 1]' | jq .result{
"ops": [
{
"op_id": "2f9117f181b5ea9b3269d91503bb564e77bd4eae54d8a6f48f4d6c77f1e1cc3d",
"status": "fast_confirmed",
"block": {
"height": "274",
"hash": "f337432055aaa71728d34ec078cc9b426654829790d8d39011e0ebd131b86a9a"
},
"index": 0,
"position": "274:0:0"
}
],
"next": "274:0:0",
"at": {
"height": "286",
"hash": "2c1363cf56e91f58e8bd2167b74625120771ad3b0956ea215de540faa33df3ad"
}
}Blocks and settlements
ksana_getBlock
Parameter: a block identifier. The result holds hash, header, entries, settlement, receipts, accepted_height and merged. The example leaves out entries and receipts to stay short:
rpc $NODE ksana_getBlock '["268"]' | jq '.result | del(.entries, .receipts)'{
"hash": "571cef33ce79209d2064448531fb7759be191e5357039859445fef9154682502",
"header": {
"version": 2,
"height": "268",
"parents": [
"5f2117f25c23819c29a9be702b45c72d2d48a71d6d72094d4f58d6ee20fd3816"
],
"btc_anchor": {
"height": 326320,
"hash": "eae6f2a86febdd8ae21bee2da3902a07cb324648485c58944c708ddb0f000000"
},
"entries_root": "e4087b3dca5eef67e42073d133ab3969fb5c4306046870811059a0972567b793",
"state_root": "bd06368cf82d7ed80c172f1b4c283993941eaeaf2a10f20b05113564c567f77f",
"producer": "ccc06c3ce8d176b3c10138dd4e70873939a9c6dc05597f45aace147ef2c07d3d",
"timestamp": "1791980287",
"bits": 520436435,
"nonce": "638"
},
"settlement": null,
"accepted_height": "268",
"merged": []
}header.parentslists the block’s parents in the block DAG;btc_anchoris the Bitcoin block it anchors to;produceris the account that made it.entrieslists the block’s entries, each an object with one key:Sponsored(a path B op),Forced(a path C op with thebtc_txidthat carried it) orUnlock.receiptsholds one receipt per entry, from when the main chain accepted the block. An entry whoseerrorstarts withskippedwas not executed.settlementholds the block’s own commit and reveal transactions,{ "commit", "reveal" }, as hex, ornullwhen it carries none.accepted_heightis the height of the main-chain block that accepted this one: its own height on the main chain, the merging block’s height for a merged block,nullwhile no main-chain block has accepted it.mergedlists the blocks it merges, in the order their entries run.
The entry of block 268, without its signature:
rpc $NODE ksana_getBlock '["268"]' | jq '.result.entries[0].Sponsored.op | del(.sig)'{
"version": 2,
"chain_id": 1002,
"account": "d08a82dc475034a5f18bb589be8343472e67996b830764889bcbed12e24510bf",
"nonce": "3",
"fee": {
"Token": {
"amount": "1"
}
},
"max_gas": "1000000",
"action": {
"Call": {
"contract": "2d6e5edc46352b2931657479fce65fdbf01aafc5acc3de7c27660ce19dda8171",
"method": "add_liquidity",
"args": "00a0724e18090000000000000000000000f2052a010000000000000000000000",
"funds": [
{
"asset": "0000000000000000000000000000000000000000000000000000000000000000",
"amount": "5000000000"
}
]
}
}
}sig is {"Bip340": "<64 bytes>"} for a key signature or {"Bip322Simple": "<bytes>"} for a wallet signature. fee is {"Token": {"amount"}} for path B and "BtcNative" for path C. action is Deploy, Call or AssetTransfer.
ksana_getSettlement
Parameter: a block identifier. It tells you whether the block’s data is on Bitcoin. The example leaves out txs, the two raw transactions ({ "commit", "reveal" } in hex):
rpc $NODE ksana_getSettlement '["264"]' | jq '.result | del(.txs)'{
"block": {
"height": "264",
"hash": "429f785a8e71515aed13acad24285fa441a9975f929a98c89ea30b0939e25bd2"
},
"txid": "a7cae8b07c1c5c8fe1258dd89205d75b5d4c81219d9843bfb4f35508b9bc4ae8",
"txid_rpc": "e84abcb90855f3b4bf43989d21814c5d5bd70592d88d25e18f5c1c7cb0e8caa7",
"commit_txid": "ce91d3090919756230a46bcaf9195d375890e82b9229f8fb01f4014b7ea5c8ee",
"commit_txid_rpc": "eec8a57e4b01f401fbf829922be89058375d19f9ca6ba4306275190909d391ce",
"carrier_account": "ccc06c3ce8d176b3c10138dd4e70873939a9c6dc05597f45aace147ef2c07d3d",
"carrier": null,
"status": "confirmed",
"confirmed": {
"txid": "a7cae8b07c1c5c8fe1258dd89205d75b5d4c81219d9843bfb4f35508b9bc4ae8",
"height": 326322,
"block": "c48a4f4f1f7ce1e696becf01bbc5f15fdbf8ba7145b9badff31c7e7711000000",
"confirmations": 3
},
"settled": true,
"btc_final": false
}txidis the reveal transaction, which carries the payload in its Taproot witness;commit_txidis the commit transaction it spends. The_rpcfields give them in explorer order.statusispending(not broadcast yet: a settlement waits until the block has 2 fast-lane confirmations),broadcast, orconfirmed.confirmeddescribes the first confirmed Bitcoin transaction that put the block’s data on Bitcoin,{ "txid", "height", "block", "confirmations" }. That can be the block’s own settlement, a later block’s, or a republish.settledistrueonce the data of the block that accepted it, and of every main-chain block before that one, is on Bitcoin;btc_finalistrueonce the block that accepted it is final.carrier_accountis the account paid as the block’s carrier, ornullwhen the producer keeps the reward. It is answered from the current Bitcoin chain and main chain.
A block can have its body carried by a later block’s settlement (batching). Its carrier then names that block, and the transaction fields describe that block’s settlement; while no block carries it yet, they are null and status is pending. An empty block, or one holding only path C operations, has no body to land, so it returns -32004 when it carries no settlement itself.
Read-only calls
ksana_call
Parameter: one call request object. It runs a contract method on the latest state without a fee, without a nonce, and without writing anything:
| Field | Required | Meaning |
|---|---|---|
contract |
yes | the contract ID |
method |
yes | the method name |
args |
yes | the arguments, Borsh-encoded, in hex ("" for none) |
funds |
no | attached assets, [{ "asset", "amount" }], moved from caller in the simulation |
caller |
no | the simulated caller, 32 zero bytes by default |
max_gas |
no | a decimal string, at most BLOCK_GAS_LIMIT (100,000,000, also the default) |
Read a pool’s reserves (the tutorial’s pool, (u128, u128) little-endian):
POOL=2d6e5edc46352b2931657479fce65fdbf01aafc5acc3de7c27660ce19dda8171
rpc $NODE ksana_call "[{\"contract\": \"$POOL\", \"method\": \"reserves\", \"args\": \"\"}]" \
| jq .result{
"op_id": null,
"success": true,
"error": null,
"gas_used": "45885",
"output": "be310d09810400000000000000000000583a7696000000000000000000000000",
"events": []
}Attached funds come out of caller’s balance, so a simulation that attaches funds needs a funded caller. Here a quote for swapping 1 KSA in that pool, without caller, fails, and a failed call reports all of max_gas as used:
ARGS=${KSA}00e1f50500000000000000000000000001000000000000000000000000000000
rpc $NODE ksana_call "[{\"contract\": \"$POOL\", \"method\": \"swap\", \"args\": \"$ARGS\",
\"funds\": [{\"asset\": \"$KSA\", \"amount\": \"100000000\"}], \"max_gas\": \"500000\"}]" \
| jq .result{
"op_id": null,
"success": false,
"error": "attached funds transfer failed: insufficient balance",
"gas_used": "500000",
"output": "",
"events": []
}With "caller": "$ACCOUNT" added, the call succeeds and returns the quote:
{
"op_id": null,
"success": true,
"error": null,
"gas_used": "144458",
"output": "f4513fcf2b0000000000000000000000"
}(That output leaves out events.) The receipt is { "op_id", "success", "error", "gas_used", "output", "events" }, the same shape as an op’s receipt with op_id set to null. output is the hex of the bytes the method returned, and each event is { "contract", "data" }.
The call sees the environment of the next block: its height is the tip’s plus one, with the tip’s timestamp and Bitcoin anchor. native_tx() returns nothing. A max_gas above the limit returns -32602. A node runs at most 4 calls at a time; further calls return -32009 until one finishes.
Subscriptions
ksana_subscribeBlocks
Over WebSocket only. The answer is a subscription ID. Then every block that joins the main chain, including the blocks a reorganization connects, arrives as a ksana_block notification. To stop, call ksana_unsubscribeBlocks with the subscription ID.
This JavaScript runs in a browser or in Node.js 22 and later (node subscribe.mjs). It subscribes, prints the first notification, and unsubscribes:
const ws = new WebSocket("wss://signet.ksana.network/rpc");
const send = (id, method, params) =>
ws.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
ws.onopen = () => send(1, "ksana_subscribeBlocks", []);
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log(JSON.stringify(message, null, 2));
if (message.method === "ksana_block") {
// One block is enough here: unsubscribe, then close once the node confirms
send(2, "ksana_unsubscribeBlocks", [message.params.subscription]);
}
if (message.id === 2) ws.close();
};
ws.onclose = (event) => console.log("closed", event.code);{
"jsonrpc": "2.0",
"id": 1,
"result": 2924681207070251
}
{
"jsonrpc": "2.0",
"method": "ksana_block",
"params": {
"subscription": 2924681207070251,
"result": {
"height": "287",
"hash": "881c3cbbe76d3f2f9ac4f35a8400a38e0d3bb9cded775b4d1e4acf844be386e0",
"entries": 1,
"timestamp": "1791982019"
}
}
}
{
"jsonrpc": "2.0",
"id": 2,
"result": true
}
closed 1005Each notification is { "height", "hash", "entries", "timestamp" }: entries is the block’s number of entries and timestamp is in seconds.
A subscriber that falls behind skips the notifications it missed and continues with later blocks. Nothing reconnects for you: when the connection closes, open a new one, subscribe again, and read the blocks you missed with ksana_getBlock. On an idle signet, blocks can be minutes apart.
Indexer methods
The indexer rebuilds the chain from bitcoind alone and checks every state root. Its two methods follow the same conventions.
indexer_status
No parameters.
rpc $INDEXER indexer_status | jq .result{
"height": "276",
"hash": "e23875efc23a5c7807a8ebc424e9f74a292fbfdfbe5c6b0bc505ddb8719a373c",
"state_root": "7f10d03286185198f17e2c585fb698a9323f51511d8c2b2e8abb12b1d6f65e5d",
"btc_height": 326324,
"finalized": {
"height": "246",
"hash": "c45e4b58173b529c164e66c61219befe4bf7b3e93c319f9c80dbd98982793071"
},
"halted": null
}height,hashandstate_rootdescribe the highest block the indexer has executed and verified. It verifies a block once the block’s data is on Bitcoin and the block has 2 confirmations on the indexer’s main chain, or once it is BTC-final, so it trails the node.btc_heightis the Bitcoin tip it has scanned,nullbefore it has synced with bitcoind.finalizedis the finalized tip, decided by the same rule as the node’s.haltedisnullin normal operation. The indexer stops, andhaltedgives the reason, only when a block that Bitcoin made final turns out invalid; an invalid block that is not final is left out, and the indexer chooses its main chain again (alarms).
indexer_stateRoot
Parameter: a block identifier. It answers only for blocks the indexer has verified; others return -32004, and a malformed identifier returns -32602.
rpc $INDEXER indexer_stateRoot '["276"]' | jq .result
rpc $NODE ksana_getBlock '["276"]' | jq -r .result.header.state_root{
"height": "276",
"hash": "e23875efc23a5c7807a8ebc424e9f74a292fbfdfbe5c6b0bc505ddb8719a373c",
"state_root": "7f10d03286185198f17e2c585fb698a9323f51511d8c2b2e8abb12b1d6f65e5d"
}
7f10d03286185198f17e2c585fb698a9323f51511d8c2b2e8abb12b1d6f65e5dThe two state roots match: the node’s block 276 is the block the indexer rebuilt from Bitcoin alone.
Errors
An error answer has an error object instead of result, with code and message, sometimes data:
rpc $NODE ksana_getOpStatus "[\"$KSA\"]" | jq .error
rpc $NODE ksana_getBlock '["0x10"]' | jq .error
rpc $NODE ksana_getBlockCount | jq .error{
"code": -32004,
"message": "unknown op"
}
{
"code": -32602,
"message": "block identifier must be \"latest\", a decimal height or a 64-digit hex hash"
}
{
"code": -32601,
"message": "Method not found"
}| Code | Meaning |
|---|---|
-32601 |
no such method, or not available on this node (the op listings without an op index) |
-32602 |
a malformed parameter: hex, decoding, length, range |
-32010 |
the op breaks a consensus rule, or is a path C op |
-32011 |
the node’s policy refuses the op: fee too low, pool full, replacement fee not higher |
-32004 |
the object does not exist: op, block, asset, settlement, unverified block |
-32009 |
the node is busy: too many ksana_call requests at once; try again later |
-32603 |
an internal error of the node |