Communiquer avec votre nœud Dusk

Dusk Logo

Site web : dusk.network

Voici différentes manières de questionner votre nœud Dusk : GraphQL, RPC et RUES.

Astuce

🇬🇧 Version anglaise : How to query your Dusk node?

🫶 Adresse Dusk pour les pourboires
VKZpBrNtEeTobMgYkkdcGiZn8fK2Ve2yez429yRXrH4nUUDTuvr7Tv74xFA2DKNVegtF6jaom2uacZMm8Z2Lg2J

1️⃣ GraphQL

Lister les fonctions GraphQL disponibles et leurs structures de données

curl -X POST 'https://nodes.dusk.network/on/graphql/query'

Récupérer le dernier numéro de bloc

curl 'https://nodes.dusk.network/on/graphql/query' --json \
   'query { block(height: -1) { header { height } } }'

Exemple de réponse :

{
    "block": {
        "header": {
            "height": 189268
        }
    }
}

Récupérer les N derniers blocs

Plutôt que de récupérer trop d’informations, ici, nous ne voulons que le numéro du bloc (height) et l’adresse du nœud (generatorBlsPubkey) qui l’a validé.

Vous pouvez modifier le nombre de blocs à la ligne last: 100 (pour les 100 derniers blocs, par exemple).

curl 'https://nodes.dusk.network/on/graphql/query' --json \
   'fragment BlockInfo on Block { header { height, generatorBlsPubkey } }
    query() { blocks(last: 100) {...BlockInfo} }'

Le même type de requête peut être effectué en spécifiant le nombre de blocs dans un entête HTTP spécial rusk-gqlvar-XXX (où XXX est le nom de la variable) :

curl 'https://nodes.dusk.network/on/graphql/query' \
    -H 'rusk-gqlvar-count: 100' \
    --json \
        'fragment BlockInfo on Block { header { height, generatorBlsPubkey } }
         query($count: Int!) { blocks(last: $count) {...BlockInfo} }'

Exemple de réponse :

{
    "blocks": [
        {
            "header": {
                "generatorBlsPubkey": "xY8e71u5E8CFmSU1...",
                "height": 189291
            }
        },
        {
            "header": {
                "generatorBlsPubkey": "ueu27WiUjcssYMvR...",
                "height": 189290
            }
        },
        // (...)
    ]
}

Récupérer les N derniers blocs avec les détails des transactions

curl 'https://nodes.dusk.network/on/graphql/query' --json \
   'fragment TransactionInfo on SpentTransaction { tx { callData { contractId, data, fnName }, txType }}
    fragment BlockInfo on Block { header { height, generatorBlsPubkey }, transactions {...TransactionInfo} }
    query() { blocks(last: 100) {...BlockInfo} }'

Exemple de réponse :

{
    "blocks": [
        {
            "header": {
                "generatorBlsPubkey": "24e5SaodhsLnTpHW...",
                "height": 189314
            },
            "transactions": [
                {
                    "tx": {
                        "callData": {
                            "contractId": "0200000000000000000000000000000000000000000000000000000000000000",
                            "data": "0000000000000000b574dad75d051d3993fb9ba7c36453ca9c10d63fc48b0d924144d...",
                            "fnName": "stake"
                        },
                        "txType": "Moonlight"
                    }
                }
            ]
        },
        // (...)
    ]
}

Récupérer les N derniers blocs avec les détails des transactions au format JSON

curl 'https://nodes.dusk.network/on/graphql/query' --json \
   'fragment TransactionInfo on SpentTransaction { tx { json } }
    fragment BlockInfo on Block { header { height, generatorBlsPubkey }, transactions {...TransactionInfo} }
    query() { blocks(last: 100) {...BlockInfo} }'

Exemple de réponse :

{
    "blocks": [
        {
            "header": {
                "generatorBlsPubkey": "24e5SaodhsLnTpHW...",
                "height": 189314
            },
            "transactions": [
                {
                    "tx": {
                        "json": "{\"type\":\"moonlight\",\"sender\":\"21SvzTdXggQkZpnk...\",\"receiver\":null,\"value\":0,\"nonce\":35,\"deposit\":1070000000000,\"fee\":{\"gas_price\":\"1\",\"refund_address\":\"21SvzTdXggQkZpnk...\",\"gas_limit\":\"2000000000\"},\"call\":{\"contract\":\"0200000000000000000000000000000000000000000000000000000000000000\",\"fn_name\":\"stake\",\"fn_args\":\"AAAAAAAAAAC1dNrXXQUdOZP7m6fDZFPKnBDWP8SLDZJBRN2nzm4EPSMTh16bHMpQU\"},\"is_deploy\":false,\"memo\":null}"
                    }
                }
            ]
        },
        // (...)
    ]
}

Et en parsant les données JSON, on obtient ceci :

{
    "call": {
        "contract": "0200000000000000000000000000000000000000000000000000000000000000",
        "fn_args": "AAAAAAAAAAC1dNrXXQUdOZP7m6fDZFPKnBDWP8SLDZJBRN2nzm4EPSMTh16bHMpQU...",
        "fn_name": "stake"
    },
    "deposit": 1070000000000,
    "fee": {
        "gas_limit": "2000000000",
        "gas_price": "1",
        "refund_address": "21SvzTdXggQkZpnk..."
    },
    "is_deploy": false,
    "memo": null,
    "nonce": 35,
    "receiver": null,
    "sender": "21SvzTdXggQkZpnk...",
    "type": "moonlight",
    "value": 0
}

Récupérer un ensemble de blocs

Exemple avec la récupération des blocs 10, 11 et 12 : la syntaxe est [10, 12].

curl 'https://nodes.dusk.network/on/graphql/query' --json \
   'fragment BlockInfo on Block { header { height, generatorBlsPubkey } }
    query() { blocks(range: [10, 12]) {...BlockInfo} }'

Exemple de réponse :

{
    "blocks": [
        {
            "header": {
                "generatorBlsPubkey": "25hxg2DMEmj1ghG8...",
                "height": 10
            }
        },
        {
            "header": {
                "generatorBlsPubkey": "od2bVPYstYud4N1G...",
                "height": 11
            }
        },
        {
            "header": {
                "generatorBlsPubkey": "nw2fQCAYb5S5v9eM...",
                "height": 12
            }
        }
    ]
}

Récupérer les données JSON des transactions pour un bloc donné

Le bloc en question est le numéro 189314.

curl 'https://nodes.dusk.network/on/graphql/query' --json \
   'fragment TransactionInfo on SpentTransaction { tx { json } }
    fragment BlockInfo on Block { transactions {...TransactionInfo} }
    query() { block(height: 189314) {...BlockInfo} }'

Exemple de réponse :

{
    "block": {
        "transactions": [
            {
                "tx": {
                    "json": "{\"type\":\"moonlight\",\"sender\":\"21SvzTdXggQkZpnk...\",\"receiver\":null,\"value\":0,\"nonce\":35,\"deposit\":1070000000000,\"fee\":{\"gas_price\":\"1\",\"refund_address\":\"21SvzTdXggQkZpnk...\",\"gas_limit\":\"2000000000\"},\"call\":{\"contract\":\"0200000000000000000000000000000000000000000000000000000000000000\",\"fn_name\":\"stake\",\"fn_args\":\"AAAAAAAAAAC1dNrXXQUdOZP7m6fDZFPKnBDWP8SLDZJBRN2nzm4EPSMTh16bHMpQU\"},\"is_deploy\":false,\"memo\":null}"
                }
            }
        ]
    }
}

Récupérer toutes les données d’un bloc, détails des transactions inclus, pour un bloc donné

Le bloc en question est le numéro 189314.

curl 'https://nodes.dusk.network/on/graphql/query' --json \
    'fragment TransactionInfo on SpentTransaction { blockHash, blockHeight, blockTimestamp, err, gasSpent, id, tx { callData { contractId, data, fnName }, gasLimit, gasPrice, id, isDeploy, json, memo, txType } }
     fragment BlockInfo on Block { header { hash, gasLimit, height, generatorBlsPubkey, prevBlockHash, seed, stateHash, timestamp, version }, fees, gasSpent, reward, transactions {...TransactionInfo} }
     query() { block(height: 189314) {...BlockInfo} }'

Exemple de réponse :

{
    "block": {
        "fees": 25973789,
        "gasSpent": 25973789,
        "header": {
            "gasLimit": 3000000000,
            "generatorBlsPubkey": "24e5SaodhsLnTpHW...",
            "hash": "332799ed787002e6619f0f3c4f6b7fa05333881f50c5cf2f23fba35b80e734f7",
            "height": 189314,
            "prevBlockHash": "b75f26bff0c0e8e624b8d1a4b2345bd97a032e47834cb4c9625f546a37626796",
            "seed": "923e8f3196ea3be10bad98d2d322f6cbfd7e8a6732d804615929561a660cea49406f795589a86a8d400d4b6f5418aa69",
            "stateHash": "37150543eb1e6c3457f3219ee4535e4534eec7dbc118cae32d4b5ca3e90444c4",
            "timestamp": 1738145108,
            "version": 1
        },
        "reward": 19857400000,
        "transactions": [
            {
                "blockHash": "332799ed787002e6619f0f3c4f6b7fa05333881f50c5cf2f23fba35b80e734f7",
                "blockHeight": 189314,
                "blockTimestamp": 1738145108,
                "err": null,
                "gasSpent": 25973789,
                "id": "bc52d0247c70dde8707d8f49f5d656a9272962cc2e0dc3ca2192e6e117b40d59",
                "tx": {
                    "callData": {
                        "contractId": "0200000000000000000000000000000000000000000000000000000000000000",
                        "data": "0000000000000000b574dad75d051d3993fb9ba7c36453ca9c10d63fc48b0d924144dd...",
                        "fnName": "stake"
                    },
                    "gasLimit": 2000000000,
                    "gasPrice": 1,
                    "id": "bc52d0247c70dde8707d8f49f5d656a9272962cc2e0dc3ca2192e6e117b40d59",
                    "isDeploy": false,
                    "json": "{\"type\":\"moonlight\",\"sender\":\"21SvzTdXggQkZpnk...\",\"receiver\":null,\"value\":0,\"nonce\":35,\"deposit\":1070000000000,\"fee\":{\"gas_price\":\"1\",\"gas_limit\":\"2000000000\",\"refund_address\":\"21SvzTdXggQkZpnk...\"},\"call\":{\"fn_args\":\"AAAAAAAAAAC1dNrXXQUdOZP7m6fDZFPKnBDWP8SLDZJBRN2nzm4EPSMTh16bHMpQU...\",\"contract\":\"0200000000000000000000000000000000000000000000000000000000000000\",\"fn_name\":\"stake\"},\"is_deploy\":false,\"memo\":null}",
                    "memo": null,
                    "txType": "Moonlight"
                }
            }
        ]
    }
}

Récupérer tout l’historique d’une adresse

curl 'https://nodes.dusk.network/on/graphql/query' --json \
    'query { fullMoonlightHistory(address: "PUBLIC_KEY", ord: "desc") { json } }'

Exemple de réponse :

{
    "fullMoonlightHistory": {
        "json": [
            {
                "block_height": 188242,
                "events": [
                    {
                        "data": {
                            "receiver": "66RS7dEhVtDgd11j...",
                            "sender": "uTmRVArYPTGpBiuW...",
                            "value": 580885000000000
                        },
                        "target": "0100000000000000000000000000000000000000000000000000000000000000",
                        "topic": "convert"
                    },
                    {
                        "data": {
                            "gas_spent": 25614038,
                            "memo": "",
                            "receiver": null,
                            "refund_info": null,
                            "sender": "uTmRVArYPTGpBiuW...",
                            "value": 0
                        },
                        "target": "0100000000000000000000000000000000000000000000000000000000000000",
                        "topic": "moonlight"
                    }
                ],
                "origin": "f689d1948dfcd541e9038ab98a709390849332c2091aca42afd1bd667c69e469"
            },
            // (...)
        ]
    }
}

Récupérer la balance et le nonce d’un compte

Ajouté dans la version 1.0.2.

curl -X POST 'https://nodes.dusk.network/on/account:PUBLIC_KEY/status'

Output example:

{
    "balance": 260292192764359648,
    "nonce": 260
}

2️⃣ RPC

Récupérer la liste des nœuds validateurs

curl -X POST 'https://nodes.dusk.network/on/node/provisioners'

Exemple de réponse :

[
    {
        "amount": 37000000000000,
        "eligibility": 4320,
        "faults": 0,
        "hard_faults": 0,
        "key": "mAkcDzY2uVvqbEv6...",
        "locked_amt": 0,
        "owner": {
            "Account": "mAkcDzY2uVvqbEv6..."
        },
        "reward": 924095468388
    },
    // (...)
]

3️⃣ RUES

RUES pour Rusk Universal Event System est un système permettant d’écouter la blockchain pour récupérer les évènements en temps réel.

Voici du code écrit en Python, et il est nécessaire d’installer les modules requests et websockets avant d’aller plus loin.

Lister les blocs validés par un nœud spécifique

Fichier : listen.py
"""
This was part of Dusk Node Monitoring until commit 832e35ef5b30bc953ea41cf57f279993cedda6a4.
Source: https://github.com/BoboTiG/dusk-monitor
"""

import json

import requests
import websockets


async def listen_to_accepted_blocks(provisioner: str) -> None:
    ws_url = "wss://nodes.dusk.network/on"
    blocks_accepted_url = "https://nodes.dusk.network/on/blocks/accepted"

    async with websockets.connect(ws_url) as wss:
        # Get a session ID
        session_id = await wss.recv()

        # Subscribe to the accepted blocks topic
        with requests.get(blocks_accepted_url, headers={"Rusk-Session-Id": session_id}) as req:
            req.raise_for_status()

        while "listening":
            raw = await wss.recv()
            raw = raw[raw.find(b'{"header"') :]
            block = json.loads(raw)
            if block["header"]["generator_bls_pubkey"] == provisioner:
                print(block["header"]["height"])


if __name__ == "__main__":
    import asyncio
    import sys

    asyncio.run(listen_to_accepted_blocks(sys.argv[1]))

Une fois le script en fonctionnement, les numéros de blocs générés par le nœud apparaitront en temps réel :

python listen.py 'PROVISIONER_PUBLIC_KEY'

Exemple de réponse brute (il s’agit du contenu de la variable raw dans le script) :

b'k\x00\x00\x00{"Content-Location":"/on/blocks:a9e42d231967df49dba9ab1fdf5d40d68287c3af715b53cff841c7a8703bf6d2/accepted"}{"header":{"event_bloom":"000...","failed_iterations":[],"faultroot":"000...","gas_limit":3000000000,"generator_bls_pubkey":"22JbHtrL93fhFDvY...","hash":"a9e...","height":191574,"iteration":0,"prev_block_cert":{"ratification":{"aggregate_signature":"ac5...","bitset":106597789095414875},"result":{"Valid":"7e1..."},"validation":{"aggregate_signature":"844...","bitset":2232142576123535}},"prev_block_hash":"7e1...","seed":"942...","signature":"a32...","state_hash":"be8...","timestamp":1738167716,"txroot":"000...","version":1},"transactions":[]}'

Et une fois parsée, l’objet JSON suivant apparait (il s’agit du contenu de la variable block dans le script) :

{
    "header": {
        "event_bloom": "000...",
        "failed_iterations": [],
        "faultroot": "000...",
        "gas_limit": 3000000000,
        "generator_bls_pubkey": "22JbHtrL93fhFDvY...",
        "hash": "a9e...",
        "height": 191574,
        "iteration": 0,
        "prev_block_cert": {
            "ratification": {
                "aggregate_signature": "ac5...",
                "bitset": 106597789095414875
            },
            "result": {
                "Valid": "7e1..."
            },
            "validation": {
                "aggregate_signature": "844...",
                "bitset": 2232142576123535
            }
        },
        "prev_block_hash": "7e1...",
        "seed": "942...",
        "signature": "a32...",
        "state_hash": "be8...",
        "timestamp": 1738167716,
        "txroot": "000...",
        "version": 1
    },
    "transactions": []
}

📜 Historique

2025-02-14

Ajout de la section GraphQL Récupérer la balance et le nonce d’un compte (rusk 1.0.2).

2025-02-03

Remplacement de l’argument --data-raw par --json pour les appels à curl.

2025-01-30

Ajout de la section GraphQL Lister les fonctions GraphQL disponibles et leurs structures de données.

Ajout de la section GraphQL Récupérer tout l’historique d’une adresse.

2025-01-29

Premier jet.