Esta página te guiará a través de algunas interacciones básicas con tu nodo. Esta guía debe guiarte a las herramientas adecuadas, no ser vista como referencia canónica. Consulta siempre la documentación correspondiente a la herramienta que estés utilizando:
Polkadot-JS RPC es una librería JavaScript para interactuar con el endpoint de la API RPC de Substrate, distribuida como paquete @polkadot/api
Node.js.
Substrate API Sidecar utiliza Polkadot-JS RPC para proporcionar servicios REST ejecutables por separado.
RPC de Polkadot #
El cliente Parity Polkadot expone endpoints HTTP y WS para conexiones RPC. Los puertos por defecto son 9933 para HTTP y 9944 para WS.
Para obtener una lista de todos los métodos RPC, el nodo tiene un endpoint RPC llamado rpc_methods
.
Por ejemplo, usando websocat:
echo '{"id":1,"jsonrpc":"2.0","method":"rpc_methods","params":[]}' | websocat -n1 -B 99999999 ws://127.0.0.1:9944
{"jsonrpc":"2.0","result":{"methods": ["account_nextIndex", "author_hasKey", "author_hasSessionKeys", "author_insertKey", "author_pendingExtrinsics", "author_removeExtrinsic", "author_rotateKeys", "author_submitAndWatchExtrinsic", "author_submitExtrinsic", "author_unwatchExtrinsic", "babe_epochAuthorship", "beefy_getFinalizedHead", "beefy_subscribeJustifications", "beefy_unsubscribeJustifications", "chain_getBlock", "chain_getBlockHash", "chain_getFinalisedHead", "chain_getFinalizedHead", "chain_getHead", "chain_getHeader", "chain_getRuntimeVersion", "chain_subscribeAllHeads", "chain_subscribeFinalisedHeads", "chain_subscribeFinalizedHeads", "chain_subscribeNewHead", "chain_subscribeNewHeads", "chain_subscribeRuntimeVersion", "chain_unsubscribeAllHeads", "chain_unsubscribeFinalisedHeads", "chain_unsubscribeFinalizedHeads", "chain_unsubscribeNewHeads", "chain_unsubscribeNewHeads", "chain_unsubscribeRuntimeVersion", "childstate_getKeys", "childstate_getKeysPaged", "childstate_getKeysPagedAt", "childstate_getStorage", "childstate_getStorageEntries", "childstate_getStorageHash", "childstate_getStorageSize", "grandpa_proveFinality", "grandpa_roundState", "grandpa_subscribeJustifications", "grandpa_unsubscribeJustifications", "mmr_generateBatchProof", "mmr_generateProof", "offchain_localStorageGet", "offchain_localStorageSet", "payment_queryFeeDetails", "payment_queryInfo", "state_call", "state_callAt", "state_getChildReadProof", "state_getKeys", "state_getKeysPaged", "state_getKeysPagedAt", "state_getMetadata", "state_getPairs", "state_getReadProof", "state_getRuntimeVersion", "state_getStorage", "state_getStorageAt", "state_getStorageHash", "state_getStorageHashAt", "state_getStorageSize", "state_getStorageSizeAt", "state_queryStorage", "state_queryStorageAt", "state_subscribeRuntimeVersion", "state_subscribeStorage", "state_traceBlock", "state_trieMigrationStatus", "state_unsubscribeRuntimeVersion", "state_unsubscribeStorage", "subscribe_newHead", "sync_state_genSyncSpec", "system_accountNextIndex", "system_addLogFilter", "system_addReservedPeer", "system_chain", "system_chainType", "system_dryRun", "system_dryRunAt", "system_health", "system_localListenAddresses", "system_localPeerId", "system_name", "system_nodeRoles", "system_peers", "system_properties", "system_removeReservedPeer", "system_reservedPeers", "system_resetLogFilter", "system_syncState", "system_unstable_networkState", "system_version", "unsubscribe_newHead"], "version": 1}, "id":1}
Ten en cuenta que esta llamada mostrará incluso aquellos métodos RPC que estén desactivados por una flag de seguridad como --rpc-methods Safe
. Se está trabajando en ello.
Añade parámetros en la llamada, por ejemplo obtén un bloque por su valor hash:
echo '{"id":1,"jsonrpc":"2.0","method":"chain_getBlock","params":["0x7d4ef171d483d37aa2339877524f0731af98e367c38f8fa27f133193ed2b5615"]}' | websocat -n1 -B 99999999 ws://127.0.0.1:9944
{"jsonrpc": "2. 0","result":{"block":{"header":{"parentHash":"0xb5e10293122a3c706dfcf5c0e89d5fb90929e7ee580c5167e439afa330fae2c7","number":"0xbb07fe","stateRoot": "0x872dfbb3516a6e3b9becf01bb2192e53a1d77ef6c37e426f03ebf64b33a68ede","extrinsicsRoot":"0xe131e6af57c503ca6c6a151b2e621d05f65ef7be07e24abc2444fa1eb67c444a","digest":{"logs": ["0x0642414245b50103b9000000ebdf8810000000002621c85fe312c4b8b9db111b9311a2857e265a62c7bd5a9b08f3e0989e51ea619481408decdc83f0f1322b706b50904f692f3c2dd505e7633dc029ca38a3f40072e7378760cf44e83566ec92ee330042d916684e957399badba91ed342a3270d", "0x0542414245010190e94b9f1af95ae7645f85dc3d49f4c73dcce31083c9e1f712523a9b132aff798f89e0e6146429a869dde4ee060e7630831890f15942d5889ac4dfa24150368a"]}},"extrinsics": ["0x280403000bd61300888301",". .."]}, "justifications":null}, "id":1}
Algunos valores de retorno pueden no parecer significativos a primera vista. Polkadot utiliza la codificación SCALE como formato adecuado para entornos de ejecución con recursos limitados. Tendrás que decodificar la información y utilizar los metadatos de la cadena (state_getMetadata
) para obtener información legible por humanos.
Seguimiento del chain head #
Utiliza el endpoint RPC chain_subscribeFinalizedHeads
para suscribirte a un stream (flujo) de hashes de headers finalizados, o chain_FinalizedHeads
para obtener el último hash de la header finalizado. Utiliza chain_getBlock
para obtener el bloque asociado a un hash determinado. chain_getBlock
sólo acepta hashes de bloque, por lo que si necesitas consultar bloques intermedios, utiliza chain_getBlockHash
para obtener el hash de bloque a partir de un número de bloque.
Substrate API Sidecar #
Parity mantiene un cliente RPC, escrito en TypeScript, que expone un conjunto limitado de endpoints. Gestiona los metadatos y la lógica de los códecs para que siempre trates con información decodificada. También agrega información que una empresa de infraestructura puede necesitar para contabilidad y auditoría, por ejemplo, las tarifas de transacción.
El sidecar puede recuperar bloques, obtener el balance de una dirección de forma atómica (es decir, con un número de bloque correspondiente), obtener los metadatos de la cadena, obtener una predicción de la tarifa de transacción, calcular las recompensas de staking pendientes para una address, enviar transacciones a la cola de transacciones de un nodo, y mucho más.
El cliente se ejecuta en un host HTTP. En los siguientes ejemplos se utiliza python3, pero puedes consultar de la forma que prefieras en http://HOST:PORT/
. El valor por defecto es http://127.0.0.1:8080
.
Obtener un bloque #
Obtén un bloque utilizando el endpoint block/number
. Para obtener el extremo de cadena, omite el block number (número de bloque).
import requests
import json
url = 'http://127.0.0.1:8080/blocks/7409038'
response = requests.get(url)
if response.ok:
block_info = json.loads(response.text)
print(block_info)
Esto devuelve un bloque totalmente decodificado.
En el extrinsic balances.transfer
, el elemento partialFee
es la tarifa (fee) de la transacción. Se denomina “fee parcial” porque el fee (tarifa) total incluiría el campo tip
(propina). Nota que algunos extrinsics no tienen firma. Éstos son inherentes.
SEGUIMIENTO DE LOS FEES DE TRANSACCIÓN
Al realizar el seguimiento de los fees de transacción, el valor extrinsics.paysFee
no es suficiente para determinar si el extrinsic tenía un fee. Este campo sólo significa que requeriría un fee si se presentara como una transacción. Para cobrar un fee, también es necesario firmar una transacción. Así que en el siguiente ejemplo, el extrinsic timestamp.set
no paga un fee porque es un inherente, puesto en el bloque por el autor del bloque.
{
"number":"7409038",
"hash":"0x0e9610f3c89fac046ef83aa625ad414d5403031faa026b7ab2a918184e389968",
"parentHash":"0xba308541eb207bc639f36d392706309a031c21622f883fb07411060389c5ffdd",
"stateRoot":"0x4426383b64a944ad7222a4019aefd558c749da0c6920cfcdfd587741d54abbe2",
"extrinsicsRoot":"0x74749e5f5aeb610bc23fd6d8d79fd8bbf5e4b6053f70ba94ea6b3cc271df4b3a",
"authorId":"Fvvz6Ej1D5ZR5ZTK1vE1dCjBvkbxE1VncptEtmFaecXe4PF",
"logs":[
{
"type":"PreRuntime",
"index":"6",
"value":[
"BABE",
"0x023a0200009c7d191000000000"
]
},
{
"type":"Seal",
"index":"5",
"value":[
"BABE",
"0x2296a50fa4fea3a46a95ad5b1f09de76d22c6ed3dc6755718c976e2d14c63e4dd3c6257813d9bdc03bb180b1e20393f1558ae1204982e5c7570df393e11f908b"
]
}
],
"onInitialize":{
"events":[
]
},
"extrinsics":[
{
"method":{
"pallet":"timestamp",
"method":"set"
},
"signature":null,
"nonce":null,
"args":{
"now":"1620636072000"
},
"tip":null,
"hash":"0x8b853f49b6543e4fcbc796ad3574ea5601d2869d80629e080e501da4cb7b74b4",
"info":{
},
"events":[
{
"method":{
"pallet":"system",
"method":"ExtrinsicSuccess"
},
"data":[
{
"weight":"185253000",
"class":"Mandatory",
"paysFee":"Yes"
}
]
}
],
"success":true,
"paysFee":false
},
{
"method":{
"pallet":"balances",
"method":"transfer"
},
"signature":{
"signature":"0x94b63112648e8e692f0076fa1ccab3a04510c269d1392c1df2560503865e144e3afd578f1e37e98063b64b98a77a89a9cdc8ade579dcac0984e78d90646a052001",
"signer":{
"id":"Gr5sBB1EgdmQ7FG3Ud2BdECWQTMDXNgGPfdHMMtDsmT4Dj3"
}
},
"nonce":"12",
"args":{
"dest":{
"id":"J6ksma2jVeHRcRoYPZBkJRzRbckys7oSmgvjKLrVbj1U8bE"
},
"value":"100000000"
},
"tip":"0",
"hash":"0xfbc5e5de75d64abe5aa3ee9272a3112b3ce53710664f6f2b9416b2ffda8799c2",
"info":{
"weight":"201217000",
"class":"Normal",
"partialFee":"2583332634"
},
"events":[
{
"method":{
"pallet":"balances",
"method":"Transfer"
},
"data":[
"Gr5sBB1EgdmQ7FG3Ud2BdECWQTMDXNgGPfdHMMtDsmT4Dj3",
"J6ksma2jVeHRcRoYPZBkJRzRbckys7oSmgvjKLrVbj1U8bE",
"100000000"
]
},
{
"method":{
"pallet":"balances",
"method":"Deposit"
},
"data":[
"Fvvz6Ej1D5ZR5ZTK1vE1dCjBvkbxE1VncptEtmFaecXe4PF",
"2583332634"
]
},
{
"method":{
"pallet":"system",
"method":"ExtrinsicSuccess"
},
"data":[
{
"weight":"201217000",
"class":"Normal",
"paysFee":"Yes"
}
]
}
],
"success":true,
"paysFee":true
},
{
"method":{
"pallet":"utility",
"method":"batch"
},
"signature":{
"signature":"0x8aa2fc3f0cff52533745679523705720cff42d0e7258b9797feed193deb0ca73474726e148af0a0b096d44c07f20e5292819ec92279cffb2897e95cc337e638e",
"signer":{
"id":"F4gmSZGiM9pMYPsKW7xnGktDr4zRmN2jqy5Ze678y9YWR7F"
}
},
"nonce":"687",
"args":{
"calls":[
{
"method":{
"pallet":"staking",
"method":"payoutStakers"
},
"args":{
"validator_stash":"Cfish3zJiFnTvR9jscCap7imeA9ep3cH1wZfcZwAp2gdZHo",
"era":"2229"
}
},
{
"method":{
"pallet":"staking",
"method":"payoutStakers"
},
"args":{
"validator_stash":"Cfish3zJiFnTvR9jscCap7imeA9ep3cH1wZfcZwAp2gdZHo",
"era":"2230"
}
},
{
"method":{
"pallet":"staking",
"method":"payoutStakers"
},
"args":{
"validator_stash":"Cfish3zJiFnTvR9jscCap7imeA9ep3cH1wZfcZwAp2gdZHo",
"era":"2231"
}
},
{
"method":{
"pallet":"staking",
"method":"payoutStakers"
},
"args":{
"validator_stash":"DifishR4auphofhzxsy2aupgYo4NaUECH7qgt71CgiB2o6P",
"era":"2231"
}
},
{
"method":{
"pallet":"staking",
"method":"payoutStakers"
},
"args":{
"validator_stash":"J1fishfH94nFZLNScHgC2HorWpFD2xdPxd96wtTCHLvKxfa",
"era":"2231"
}
}
]
},
"tip":"0",
"hash":"0x69171ec3f4e5e4dfd27f4d1c5b5dbc884932c5d9a078c84495bb7ab875c8785f",
"info":{
"weight":"629782467000",
"class":"Normal",
"partialFee":"5150837715"
},
"events":[
{
"method":{
"pallet":"staking",
"method":"Reward"
},
"data":[
"Cfish3zJiFnTvR9jscCap7imeA9ep3cH1wZfcZwAp2gdZHo",
"40730624074"
]
},
{
"method":{
"pallet":"staking",
"method":"Reward"
},
"data":[
"FhLcXuFkTwyc3o9K82VBahpain1YHWyGeNMDTTyeDJKfm5b",
"4296071738"
]
},
{
"method":{
"pallet":"staking",
"method":"Reward"
},
"data":[
"F1NyXFUayqmVMdjNK45hcaTCE3JiqdU83sEGhQ3HQXn2Rpq",
"1770904403"
]
},
// ...
{
"method":{
"pallet":"utility",
"method":"BatchCompleted"
},
"data":[
]
},
{
"method":{
"pallet":"balances",
"method":"Deposit"
},
"data":[
"Fvvz6Ej1D5ZR5ZTK1vE1dCjBvkbxE1VncptEtmFaecXe4PF",
"5150837715"
]
},
{
"method":{
"pallet":"system",
"method":"ExtrinsicSuccess"
},
"data":[
{
"weight":"629782467000",
"class":"Normal",
"paysFee":"Yes"
}
]
}
],
"success":true,
"paysFee":true
}
],
"onFinalize":{
"events":[
]
},
"finalized":true
}
EL TIPO NUMÉRICO JS ES UN FLOAT DE 53 BITS DE PRECISIÓN
No se garantiza que los valores numéricos de la respuesta tengan un tipo numérico. Cualquier número mayor que 2**53-1
tendrá un tipo de string.
Envío de una transacción #
Envía una transacción serializada utilizando el endpoint transaction
con una solicitud HTTP POST.
import requests
import json
url = 'http://127.0.0.1:8080/transaction/'
tx_headers = {'Content-type' : 'application/json', 'Accept' : 'text/plain'}
response = requests.post(
url,
data='{"tx": "0xed0…000"}', # A serialized tx.
headers=tx_headers
)
tx_response = json.loads(response.text)
Si es exitoso, este endpoint devuelve un JSON con el hash de la transacción. En caso de error, devolverá un informe de error, por ejemplo:
{
"error": "Failed to parse a tx" | "Failed to submit a tx",
"cause": "Upstream error description"
}