Index API
Camino-Node can be configured to run with an indexer. That is, it saves (indexes) every container (a block, vertex or transaction) it accepts on the X-Chain, P-Chain and C-Chain. To run CaminoNode with indexing enabled, set command line flag --index-enabled to true. Camino-Node will only index containers that are accepted when running with --index-enabled
set to true. To ensure your node has a complete index, run a node with a fresh database and --index-enabled
set to true. The node will accept every block, vertex and transaction in the network history during bootstrapping, ensuring your index is complete. It is OK to turn off your node if it is running with indexing enabled. If it restarts with indexing still enabled, it will accept all containers that were accepted while it was offline. The indexer should never fail to index an accepted block, vertex or transaction.
Indexed containers (that is, accepted blocks, vertices and transactions) are timestamped with the time at which the node accepted that container. Note that if the container was indexed during bootstrapping, other nodes may have accepted the container much earlier. Every container indexed during bootstrapping will be timestamped with the time at which the node bootstrapped, not when it was first accepted by the network.
Note that for DAGs (including the X-Chain), nodes may accept vertices and transactions in a different order from one another.
If --index-enabled
is changed to false
from true
, Camino-Node won't start as doing so would cause a previously complete index to become incomplete, unless the user explicitly says to do so with --index-allow-incomplete
. This protects you from accidentally running with indexing disabled, after previously running with it enabled, which would result in an incomplete index.
This document shows how to query data from Camino-Node's Index API. The Index API is only available when running with --index-enabled
.
Format
This API uses the json 2.0
RPC format. For more information on making JSON RPC calls, see here.
Endpoints
Each chain has one or more index. To see if a C-Chain block is accepted, for example, send an API call to the C-Chain block index. To see if an X-Chain vertex is accepted, for example, send an API call to the X-Chain vertex index.
X-Chain Transactions
/ext/index/X/tx
X-Chain Vertices
/ext/index/X/vtx
P-Chain Blocks
/ext/index/P/block
C-Chain Blocks
/ext/index/C/block
Serialization of P/C Blocks
In general block container are stored in the index storage in the binary
representation of a proposer block.
block.Block() bytes can then be unmarshalled into platformVM.Block for the P-Chain or RLP decoded into types.Block from caminoethvm for C-Chain.
Implementation can be found in magellan stream indexers.
The container with index 0 on C-Chain is not wrapped into a proposer Block. You can directly RLP decode the container into a types.Block struct
API Methods
index.getLastAccepted
Get the most recently accepted container.
Signature
index.getLastAccepted({
encoding:string
}) -> {
id: string,
bytes: string,
timestamp: string,
encoding: string,
index: string
}
where:
id
is the container's IDbytes
is the byte representation of the containertimestamp
is the time at which this node accepted the containerindex
is how many containers were accepted in this index before this oneencoding
is"cb58"
or"hex"
Example Call
curl --location --request POST 'localhost:9650/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.getLastAccepted",
"params": {
"encoding":"cb58"
},
"id": 1
}'
Example Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"bytes": "111115HRzXVDSeonLBcv6QdJkQFjPzPEobMWy7PyGuoheggsMCx73MVXZo2hJMEXXvR5gFFasTRJH36aVkLiWHtTTFcghyFTqjaHnBhdXTRiLaYcro3jpseqLAFVn3ngnAB47nebQiBBKmg3nFWKzQUDxMuE6uDGXgnGouDSaEKZxfKreoLHYNUxH56rgi5c8gKFYSDi8AWBgy26siwAWj6V8EgFnPVgm9pmKCfXio6BP7Bua4vrupoX8jRGqdrdkN12dqGAibJ78Rf44SSUXhEvJtPxAzjEGfiTyAm5BWFqPdheKN72HyrBBtwC6y7wG6suHngZ1PMBh93Ubkbt8jjjGoEgs5NjpasJpE8YA9ZMLTPeNZ6ELFxV99zA46wvkjAwYHGzegBXvzGU5pGPbg28iW3iKhLoYAnReysY4x3fBhjPBsags37Z9P3SqioVifVX4wwzxYqbV72u1AWZ4JNmsnhVDP196Gu99QTzmySGTVGP5ABNdZrngTRfmGTFCRbt9CHsgNbhgetkxbsEG7tySi3gFxMzGuJ2Npk2gnSr68LgtYdSHf48Ns",
"timestamp": "2021-04-02T15:34:00.262979-07:00",
"encoding": "cb58",
"index": "0"
}
}
index.getContainerByIndex
Get container by index. The first container accepted is at index 0, the second is at index 1, etc.
Signature
index.getContainerByIndex({
index: uint64,
encoding: string
}) -> {
id: string,
bytes: string,
timestamp: string,
encoding: string,
index: string
}
id
is the container's IDbytes
is the byte representation of the containertimestamp
is the time at which this node accepted the containerindex
is how many containers were accepted in this index before this oneencoding
is"cb58"
or"hex"
Example Call
curl --location --request POST 'localhost:9650/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.getContainerByIndex",
"params": {
"index":0,
"encoding":"cb58"
},
"id": 1
}'
Example Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"bytes": "111115HRzXVDSeonLBcv6QdJkQFjPzPEobMWy7PyGuoheggsMCx73MVXZo2hJMEXXvR5gFFasTRJH36aVkLiWHtTTFcghyFTqjaHnBhdXTRiLaYcro3jpseqLAFVn3ngnAB47nebQiBBKmg3nFWKzQUDxMuE6uDGXgnGouDSaEKZxfKreoLHYNUxH56rgi5c8gKFYSDi8AWBgy26siwAWj6V8EgFnPVgm9pmKCfXio6BP7Bua4vrupoX8jRGqdrdkN12dqGAibJ78Rf44SSUXhEvJtPxAzjEGfiTyAm5BWFqPdheKN72HyrBBtwC6y7wG6suHngZ1PMBh93Ubkbt8jjjGoEgs5NjpasJpE8YA9ZMLTPeNZ6ELFxV99zA46wvkjAwYHGzegBXvzGU5pGPbg28iW3iKhLoYAnReysY4x3fBhjPBsags37Z9P3SqioVifVX4wwzxYqbV72u1AWZ4JNmsnhVDP196Gu99QTzmySGTVGP5ABNdZrngTRfmGTFCRbt9CHsgNbhgetkxbsEG7tySi3gFxMzGuJ2Npk2gnSr68LgtYdSHf48Ns",
"timestamp": "2021-04-02T15:34:00.262979-07:00",
"encoding": "cb58",
"index": "0"
}
}
index.getContainerByID
Get container by ID.
Signature
index.getContainerByID({
containerID: string,
encoding: string
}) -> {
id: string,
bytes: string,
timestamp: string,
encoding: string,
index: string
}
id
is the container's IDbytes
is the byte representation of the containertimestamp
is the time at which this node accepted the containerindex
is how many containers were accepted in this index before this oneencoding
is"cb58"
or"hex"
Example Call
curl --location --request POST 'localhost:9650/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.getContainerByID",
"params": {
"containerID": "6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"encoding":"hex"
},
"id": 1
}'
Example Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"bytes": "111115HRzXVDSeonLBcv6QdJkQFjPzPEobMWy7PyGuoheggsMCx73MVXZo2hJMEXXvR5gFFasTRJH36aVkLiWHtTTFcghyFTqjaHnBhdXTRiLaYcro3jpseqLAFVn3ngnAB47nebQiBBKmg3nFWKzQUDxMuE6uDGXgnGouDSaEKZxfKreoLHYNUxH56rgi5c8gKFYSDi8AWBgy26siwAWj6V8EgFnPVgm9pmKCfXio6BP7Bua4vrupoX8jRGqdrdkN12dqGAibJ78Rf44SSUXhEvJtPxAzjEGfiTyAm5BWFqPdheKN72HyrBBtwC6y7wG6suHngZ1PMBh93Ubkbt8jjjGoEgs5NjpasJpE8YA9ZMLTPeNZ6ELFxV99zA46wvkjAwYHGzegBXvzGU5pGPbg28iW3iKhLoYAnReysY4x3fBhjPBsags37Z9P3SqioVifVX4wwzxYqbV72u1AWZ4JNmsnhVDP196Gu99QTzmySGTVGP5ABNdZrngTRfmGTFCRbt9CHsgNbhgetkxbsEG7tySi3gFxMzGuJ2Npk2gnSr68LgtYdSHf48Ns",
"timestamp": "2021-04-02T15:34:00.262979-07:00",
"encoding": "hex",
"index": "0"
}
}
index.getContainerRange
Returns containers with indices in [startIndex
, startIndex+1
, ... , startIndex
+ numToFetch
- 1]. numToFetch
must be in [0,1024]
.
Signature
index.getContainerRange({
startIndex: uint64,
numToFetch: uint64,
encoding: string
}) -> []{
id: string,
bytes: string,
timestamp: string,
encoding: string,
index: string
}
id
is the container's IDbytes
is the byte representation of the containertimestamp
is the time at which this node accepted the containerindex
is how many containers were accepted in this index before this oneencoding
is"cb58"
or"hex"
Example Call
curl --location --request POST 'localhost:9650/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.getContainerRange",
"params": {
"startIndex":0,
"numToFetch":100,
"encoding":"cb58"
},
"id": 1
}'
Example Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"id": "6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"bytes": "111115HRzXVDSeonLBcv6QdJkQFjPzPEobMWy7PyGuoheggsMCx73MVXZo2hJMEXXvR5gFFasTRJH36aVkLiWHtTTFcghyFTqjaHnBhdXTRiLaYcro3jpseqLAFVn3ngnAB47nebQiBBKmg3nFWKzQUDxMuE6uDGXgnGouDSaEKZxfKreoLHYNUxH56rgi5c8gKFYSDi8AWBgy26siwAWj6V8EgFnPVgm9pmKCfXio6BP7Bua4vrupoX8jRGqdrdkN12dqGAibJ78Rf44SSUXhEvJtPxAzjEGfiTyAm5BWFqPdheKN72HyrBBtwC6y7wG6suHngZ1PMBh93Ubkbt8jjjGoEgs5NjpasJpE8YA9ZMLTPeNZ6ELFxV99zA46wvkjAwYHGzegBXvzGU5pGPbg28iW3iKhLoYAnReysY4x3fBhjPBsags37Z9P3SqioVifVX4wwzxYqbV72u1AWZ4JNmsnhVDP196Gu99QTzmySGTVGP5ABNdZrngTRfmGTFCRbt9CHsgNbhgetkxbsEG7tySi3gFxMzGuJ2Npk2gnSr68LgtYdSHf48Ns",
"timestamp": "2021-04-02T15:34:00.262979-07:00",
"encoding": "cb58",
"index": "0"
}
]
}
index.getIndex
Get a container's index.
Signature
index.getIndex({
containerID: string,
encoding: string
}) -> {
index: string
}
where encoding
is "cb58"
or "hex"
.
Example Call
curl --location --request POST 'localhost:9650/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.getIndex",
"params": {
"containerID":"6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"encoding":"cb58"
},
"id": 1
}'
Example Response
{
"jsonrpc": "2.0",
"result": {
"index": "0"
},
"id": 1
}
index.isAccepted
Returns true if the container is in this index.
Signature
index.isAccepted({
containerID: string,
encoding: string
}) -> {
isAccepted: bool
}
Example Call
curl --location --request POST 'localhost:9650/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.isAccepted",
"params": {
"containerID":"6fXf5hncR8LXvwtM8iezFQBpK5cubV6y1dWgpJCcNyzGB1EzY",
"encoding":"cb58"
},
"id": 1
}'
Example Response
{
"jsonrpc": "2.0",
"result": {
"isAccepted": true
},
"id": 1
}
Example: Iterating Through X-Chain Transaction
Here is an example of how to iterate through all transactions on the X-Chain.
To help users to try out this example and other index APIs, we have set up a testing indexer node located at (tbc). This indexer node is not for production use. We may change or shut it down at any time without notice.
You can use the Index API to get the ID of every transaction that has been accepted on the X-Chain, and use the X-Chain API method avm.getTx
to get a human-readable representation of the transaction.
To get an X-Chain transaction by its index (the order it was accepted in), use Index API method index.getlastaccepted.
For example, to get the second transaction (note that "index":1
) accepted on the X-Chain, do:
curl --location --request POST 'https://indexer-test.camino.network/ext/index/X/tx' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "index.getContainerByIndex",
"params": {
"encoding":"hex",
"index":1
},
"id": 1
}'
This returns the ID of the second transaction accepted in the X-Chain's history. To get the third transaction on the X-Chain, use "index":2
, and so on.
The above API call gives the response below:
{
"jsonrpc": "2.0",
"result": {
"id": "ZGYTSU8w3zUP6VFseGC798vA2Vnxnfj6fz1QPfA9N93bhjJvo",
"bytes": "0x00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000221e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000070000000129f6afc0000000000000000000000001000000017416792e228a765c65e2d76d28ab5a16d18c342f21e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000700000222afa575c00000000000000000000000010000000187d6a6dd3cd7740c8b13a410bea39b01fa83bb3e000000016f375c785edb28d52edb59b54035c96c198e9d80f5f5f5eee070592fe9465b8d0000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000223d9ab67c0000000010000000000000000000000010000000900000001beb83d3d29f1247efb4a3a1141ab5c966f46f946f9c943b9bc19f858bd416d10060c23d5d9c7db3a0da23446b97cd9cf9f8e61df98e1b1692d764c84a686f5f801a8da6e40",
"timestamp": "2021-11-04T00:42:55.01643414Z",
"encoding": "hex",
"index": "1"
},
"id": 1
}
The ID of this transaction is ZGYTSU8w3zUP6VFseGC798vA2Vnxnfj6fz1QPfA9N93bhjJvo
.
To get the transaction by its ID, use API method avm.getTx
:
curl -X POST --data '{
"jsonrpc":"2.0",
"id" :1,
"method" :"avm.getTx",
"params" :{
"txID":"ZGYTSU8w3zUP6VFseGC798vA2Vnxnfj6fz1QPfA9N93bhjJvo",
"encoding": "json"
}
}' -H 'content-type:application/json;' https://api.camino.network/ext/bc/X
Response:
{
"jsonrpc": "2.0",
"result": {
"tx": {
"unsignedTx": {
"networkID": 1,
"blockchainID": "2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM",
"outputs": [
{
"assetID": "o8seyjX6WupqJ1CE8CeaozK13kqVgc4DFvdvc4crfacLFBauW",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"output": {
"addresses": [
"X-columbus1wst8jt3z3fm9ce0z6akj3266zmgccdp03hjlaj"
],
"amount": 4999000000,
"locktime": 0,
"threshold": 1
}
},
{
"assetID": "o8seyjX6WupqJ1CE8CeaozK13kqVgc4DFvdvc4crfacLFBauW",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"output": {
"addresses": [
"X-columbus1slt2dhfu6a6qezcn5sgtagumq8ag8we75f84sw"
],
"amount": 2347999000000,
"locktime": 0,
"threshold": 1
}
}
],
"inputs": [
{
"txID": "qysTYUMCWdsR3MctzyfXiSvoSf6evbeFGRLLzA4j2BjNXTknh",
"outputIndex": 0,
"assetID": "o8seyjX6WupqJ1CE8CeaozK13kqVgc4DFvdvc4crfacLFBauW",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"input": {
"amount": 2352999000000,
"signatureIndices": [0]
}
}
],
"memo": "0x"
},
"credentials": [
{
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"credential": {
"signatures": [
"0xbeb83d3d29f1247efb4a3a1141ab5c966f46f946f9c943b9bc19f858bd416d10060c23d5d9c7db3a0da23446b97cd9cf9f8e61df98e1b1692d764c84a686f5f801"
]
}
}
]
},
"encoding": "json"
},
"id": 1
}