Dusk - GraphQL RPC
Tags: dusk, rpc, graphql, rwa
Published: 2023-12-31 17:56:42

**Explore the Dusk node's RPC capabilities, especially GraphQL... in under 10 minutes.**

Use the setup as described previously [to run a local node](https://dev.to/dusktastic/dusk-running-a-local-node-1723), regenerate the initial state, the file examples/genesis.toml  has been edited to give 100,000 coins to our wallet.
 
```
# cd rusk
# rm /tmp/example.state
# cargo r --release -p rusk -- recovery-state \
  --init examples/genesis.toml \
  -o /tmp/example.state
```

Start the node:

```
# export DUSK_CONSENSUS_KEYS_PASS=password
# ./rusk/target/release/rusk \
  -s /tmp/example.state \
  --http-listen-addr 0.0.0.0:4321
```

We will use the [same wallet as before](https://dev.to/dusktastic/dusk-running-a-local-node-1723), but the wallet software has local caching, so after resetting state we need to clear that cache and restart the wallet:

```
# rm ~/.dusk/rusk-wallet/cache
# ./wallet-cli/target/release/rusk-wallet \
  --state http://127.0.0.1:4321 \
  --prover http://127.0.0.1:4321
```

When restarting the wallet there will be some delay while it reads the chain to find the notes associated with the wallet.

Make an RPC call to the node, like this:

```
# curl -X POST http://127.0.0.1:4321/2/Chain \
  -H 'Content-Type: application/json' \
  -d '{"topic":"info","data":""}'
> {"bootstrapping_nodes":[],"chain_id":null,"kadcast_address":"127.0.0.1:9000","version":"0.7.0","version_build":"0.7.0 (16cd2d58 2023-12-31)"}
```

This call returns some basic information including version and build. There are some parts that are noteworthy; the digit in the url (in this case 2) can be 1, 2, or 3 (contract, host, debugger); the final part of the url, in the event the digit is 2 can be "chain" or "rusk". If "chain" then the topic passed can be any of "info", "gas", "alive_nodes", "propagate_tx", or "gql". The data field contains arguments required for the call (for "info", there are no arguments)...

Where the topic is "gql" we are passing a Graph Query Language call. This is what we are interested in.

Passing the topic without data returns the schema:

```
# curl -s -X POST http://127.0.0.1:4321/2/Chain \
  -d '{"topic":"gql","data":""}' \
  -H 'Content-Type: application/json'

type Block {
	header: Header!
	transactions: [SpentTransaction!]!
	reward: Int!
	fees: Int!
	gasSpent: Int!
}

type CallData {
	contractId: String!
	fnName: String!
	data: String!
}

type Header {
	version: Int!
	height: Int!
	prevBlockHash: String!
	timestamp: Int!
	hash: String!
	stateHash: String!
	generatorBlsPubkey: String!
	txRoot: String!
	gasLimit: Int!
	seed: String!
	iteration: Int!
}

type Query {
	block(height: Float, hash: String): Block
	tx(hash: String!): SpentTransaction
	transactions(last: Int!): [SpentTransaction!]!
	blockTxs(last: Int, range: [Int!], contract: String): [SpentTransaction!]!
	blocks(last: Int, range: [Int!]): [Block!]!
	mempoolTxs: [Transaction!]!
	mempoolTx(hash: String!): Transaction
}

type SpentTransaction {
	tx: Transaction!
	err: String
	gasSpent: Int!
	blockHash: String!
	blockHeight: Int!
	blockTimestamp: Int!
	id: String!
	raw: String!
}

type Transaction {
	raw: String!
	id: String!
	gasLimit: Int!
	gasPrice: Int!
	callData: CallData
}

schema {
	query: Query
}
```

You can see the queries that are available. For example:

```
transactions(last: Int!): [SpentTransaction!]!
```

This query returns the last N transactions. We can test this by running the query before any transactions are made:

```
# curl -X POST http://127.0.0.1:4321/2/Chain \
  -d '{"topic":"gql","data":"query {transactions(last: 10){id}} "}' \
  -H 'Content-Type: application/json'
> {"transactions":[]}
```

Now do two transfers from the wallet to some other address, for example 3uVEjrfzdhjN1a5o48SpiVU9AFzF5o9FZ9mFR9GfeV7U3nfDpDGfVrLJveFPokHsQiXUjjrQHYZSWNz4PY2UQ4xU, note the transaction hashes:

Transfer 100 - txid 8ffa359e732ccad500b16996662a6edb2033d3ecfa868cf68cbadc4ca4e7760d

Transfer 200 - txid
95f58e4b8e7fc5658540cd01064ae93ab07d34c1a91d5a848d32384a3951b100

And finally, execute the query again:

```
# curl -X POST http://127.0.0.1:4321/2/Chain \
  -d '{"topic":"gql","data":"query {transactions(last: 10){id}} "}' \
  -H 'Content-Type: application/json'
> {"transactions":[{"id":"95f58e4b8e7fc5658540cd01064ae93ab07d34c1a91d5a848d32384a3951b100"},{"id":"8ffa359e732ccad500b16996662a6edb2033d3ecfa868cf68cbadc4ca4e7760d"}]}
```
The transactions can be seen, newest first.

At this time the node is ephemeral, so if it is restarted, this information is no longer available. The node should soon be runnable in "persistent" mode, in which case this would not apply.

You can see the query part of the curl call, this string is in Graph Query Language, I have hidden the rest of the curl call using a small script:

```
# cat ./run_query

#!/usr/bin/perl -w
my $cmd = q#curl -s -X POST http://127.0.0.1:4321/2/Chain -d '{"topic":"gql","data":"#;
$cmd .= $ARGV[0];
$cmd .= q#"}' -H 'Content-Type: application/json'#;
print `$cmd`;
```

From this point on, I just give that string, not the entire call, which remains constant. Also we can use the Linux command **jq** to format the json output.

The query just executed returns "SpentTransaction" objects, we returned only the "id" field. It is only allowed to return fields, not objects.

Here we return the Block Height and Block hash that the most recent transaction was mined:

```
# ./run_query "query {transactions(last: 1){blockHeight, blockHash}}"  | jq
{
  "transactions": [
    {
      "blockHash": "0925fc9ff94d445ffecead0e0bfeda70ca184e74d101fbc41a7cc4e5d4412060",
      "blockHeight": 184
    }
  ]
}
```

From the schema, we see objects can include nested objects. Here we return attributes from the enclosed Transaction object; the Gas Limit and Gas Price:

```
# ./run_query "query {transactions(last: 1){tx{gasLimit, gasPrice}}}"  | jq
{
  "transactions": [
    {
      "tx": {
        "gasLimit": 500000000,
        "gasPrice": 1
      }
    }
  ]
}
```

NB: the Gas Price is not in Dusk, but in the smallest fraction of the token, called Lux.

Let's try a different query. Plugging in the Block Height returned previously, we know what Block Hash to expect:

```
# ./run_query "query {block(height: 184){header{hash}}}" | jq
{
  "block": {
    "header": {
      "hash": "0925fc9ff94d445ffecead0e0bfeda70ca184e74d101fbc41a7cc4e5d4412060"
    }
  }
}
```

Or using the Block Hash to get the block reward:

```
# ./run_query 'query {block(hash: \"0925fc9ff94d445ffecead0e0bfeda70ca184e74d101fbc41a7cc4e5d4412060\"){reward}}' | jq
{
  "block": {
    "reward": 16000000000
  }
}
```

Or fees:

```
# ./run_query 'query {block(hash: \"0925fc9ff94d445ffecead0e0bfeda70ca184e74d101fbc41a7cc4e5d4412060\"){fees}}' | jq
{
  "block": {
    "fees": 283239
  }
}
```

Or the Ids of transactions in a block:

```
# ./run_query "query {block(height: 184){transactions{id}}}" | jq
{
  "block": {
    "transactions": [
      {
        "id": "95f58e4b8e7fc5658540cd01064ae93ab07d34c1a91d5a848d32384a3951b100"
      }
    ]
  }
}
```

That's all folks.