API reference¶
Everything here is importable from robonomicsinterface; the names listed in
__all__ are the stable API (see the migration guide).
Client¶
- class robonomicsinterface.RobonomicsClient(endpoints='wss://polkadot.rpc.robonomics.network/', *, timeout=30.0, connect_timeout=10.0, retries=2, genesis_hash='0x29f4371dcc41045f5041489dfcd51389bf8ccd2161332e0de1ca803bcc3ee872', require_healthy=True, ssl=None, failback_interval=300.0, max_message_bytes=67108864, ss58_format=32)[source]¶
A connection to Robonomics that survives node failures.
- Parameters:
endpoints (str | Sequence[str]) – one URL or several, in order of preference.
ws://is fine on a local network; usewss://across the internet.timeout (float) – seconds to wait for each answer.
connect_timeout (float) – seconds to wait for a connection to open.
retries (int) – how many more times a read is tried after a network failure.
genesis_hash (str | None) – the chain every node must be on;
Noneaccepts any chain (a development node, for instance).require_healthy (bool) – refuse nodes that are syncing or have no peers.
ssl (ssl_module.SSLContext | None) – a TLS context for
wss://endpoints with a private CA.failback_interval (float) – seconds between attempts to return to a preferred endpoint, and how long a failed endpoint is skipped.
max_message_bytes (int)
ss58_format (int)
- async connect()[source]¶
Connect to the first usable endpoint;
AllEndpointsFailedif none is.- Return type:
- async request(method, params=None, *, timeout=None, retry=True)[source]¶
Send a JSON-RPC request, retrying on another endpoint if the network fails.
- async subscribe(method, params, unsubscribe)[source]¶
Start a subscription on the current connection. It is not moved if the connection drops: iteration raises
ConnectionLostand the caller decides.
- async runtime(at=None)[source]¶
The runtime metadata of block
at(the best block ifNone).- Parameters:
at (str | None)
- Return type:
Runtime
- async query(pallet, item, *keys, at=None)[source]¶
Read a storage value; see
robonomicsinterface.storage.query().
- query_map(pallet, item, *keys, at=None, page_size=100)[source]¶
Iterate over a storage map; see
robonomicsinterface.storage.query_map().
- async constant(pallet, name, *, at=None)[source]¶
A pallet constant, e.g.
await client.constant("Datalog", "WindowSize").
- async compose_call(pallet, function, args=None)[source]¶
Encode a call for the current runtime; see
compose_call().
- async sign(call, keypair, *, nonce=None, tip=0, era_period=64)[source]¶
Sign a call: the nonce from the node, a mortal era from the finalized head.
- async validate(extrinsic)[source]¶
Ask the runtime whether it would accept the extrinsic, without sending it.
This is the transaction pool’s own check, reached through
state_call(allowed on public nodes). It turns an opaque “Invalid Transaction” into a reason.Futureis not an error: the pool queues such transactions.- Raises:
InvalidTransaction – with the reason and what it usually means.
- Parameters:
extrinsic (SignedExtrinsic)
- Return type:
None
- async submit(call, keypair, *, wait_for='in_block', tip=0, era_period=64, validate=True, timeout=120.0)[source]¶
Sign, check, send and follow an extrinsic until it is in a block.
Inclusion is not success: the block’s events are read, and a failed call raises
ExtrinsicFailednaming the pallet error (RWS.NotLinkedDeviceand the like).- Raises:
InvalidTransaction – refused before sending (see
validate()).ExtrinsicFailed – included, but the call failed.
ExtrinsicDropped – the node dropped it.
ExtrinsicOutcomeUnknown – sent, but the connection or the wait ran out; it may still land, so look it up before sending it again.
- Parameters:
- Return type:
- class robonomicsinterface.RobonomicsSync(endpoints='wss://polkadot.rpc.robonomics.network/', **options)[source]¶
The blocking counterpart of
RobonomicsClient; same arguments.- property client: RobonomicsClient¶
The underlying async client (for use from its own loop only).
Pallets¶
Datalog: a per-account ring buffer of timestamped records.
Each account has WindowSize (128) slots. DatalogIndex holds start
and end: live records are at start, start+1, …, end-1 modulo the window,
oldest first. When the buffer is full, start moves on, so at most
WindowSize - 1 records are live; the slot at end may still hold an
overwritten record and is not one of them.
- class robonomicsinterface.pallets.datalog.Datalog(client)[source]¶
client.datalog: read and write datalog records.- Parameters:
client (RobonomicsClient)
- async index(address, *, at=None)[source]¶
The ring buffer bounds of
address.- Parameters:
- Return type:
- async item(address, index, *, at=None)[source]¶
The record in slot
index— 0 means slot 0 — orNoneif the slot holds no live record.- Parameters:
- Return type:
DatalogItem | None
- async latest(address, *, at=None)[source]¶
The newest record, or
Noneif there are none.- Parameters:
- Return type:
DatalogItem | None
- async items(address, *, at=None)[source]¶
All live records, oldest first, read in one request.
- Parameters:
- Return type:
- async record(keypair, data, *, subscription_owner=None, **options)[source]¶
Write a record (at most 512 bytes;
stris sent as UTF-8).- Parameters:
- Return type:
- class robonomicsinterface.pallets.datalog.DatalogIndex(start: 'int', end: 'int', window_size: 'int')[source]¶
- class robonomicsinterface.pallets.datalog.DatalogItem(index, timestamp_ms, data)[source]¶
One record: its slot, when it was written, and its bytes.
RWS: Robonomics Web Services subscriptions.
A subscription owner lists up to MaxDevicesAmount (32) device accounts;
each device can then send calls through RWS.call free of fees, paid from
the subscription’s accrued free weight. A daily subscription that has expired
stays on chain but stops accruing, so calls start failing with
FreeWeightIsNotEnough rather than NoSubscription.
- class robonomicsinterface.pallets.rws.RWS(client)[source]¶
client.rws: subscriptions, their devices and free calls.- Parameters:
client (RobonomicsClient)
- async set_devices(owner, devices, **options)[source]¶
Replace the whole device list of
owner’s subscription.Addresses are checked and normalised, duplicates dropped (order kept). An empty list removes every device.
- Raises:
TooManyDevices – more than
MaxDevicesAmountdevices.- Parameters:
- Return type:
- async add_devices(owner, *devices, **options)[source]¶
Add devices to the list: read it, extend it, write it back.
Returns
Noneif every device is already listed. Not atomic: two concurrent edits of one owner’s list can lose one of them.- Parameters:
owner (Keypair)
options (Unpack[SubmitOptions])
- Return type:
ExtrinsicResult | None
- async remove_devices(owner, *devices, **options)[source]¶
Remove devices from the list;
Noneif none of them was listed.- Parameters:
owner (Keypair)
options (Unpack[SubmitOptions])
- Return type:
ExtrinsicResult | None
- async call(device, owner, call, **options)[source]¶
Send
callasdevice, paid byowner’s subscription.- Parameters:
- Return type:
- class robonomicsinterface.pallets.rws.Ledger(kind, days, tps, issue_time_ms, last_update_ms, free_weight)[source]¶
A subscription, as
RWS.Ledgerstores it.- Parameters:
- exception robonomicsinterface.pallets.rws.TooManyDevices[source]¶
The device list would exceed the runtime’s
MaxDevicesAmount.
Accounts, balances and blocks.
- class robonomicsinterface.pallets.system.AccountInfo(nonce, consumers, providers, sufficients, free, reserved, frozen)[source]¶
System.Account: nonce, references and balance (in 10⁻⁹ XRT).- Parameters:
- class robonomicsinterface.pallets.system.Balances(client)[source]¶
client.balances: XRT transfers.- Parameters:
client (RobonomicsClient)
- async existential_deposit(*, at=None)[source]¶
The minimum balance for an account to exist (in 10⁻⁹ XRT).
- async transfer_keep_alive(sender, destination, amount, **options)[source]¶
Send
amount(10⁻⁹ XRT); refused if it would leave the sender below the existential deposit.- Parameters:
- Return type:
- class robonomicsinterface.pallets.system.Chain(client)[source]¶
client.chain: blocks and the runtime version.- Parameters:
client (RobonomicsClient)
- class robonomicsinterface.pallets.system.System(client)[source]¶
client.system: accounts.- Parameters:
client (RobonomicsClient)
Extrinsics¶
Calls, signed extrinsics, and what the chain says about them afterwards.
Everything here is offline: it takes a Runtime
and produces bytes, or takes bytes from the chain and explains them. The
client does the talking.
Signed extensions are read from the runtime metadata, in its order. Each one
contributes an explicit part (sent in the extrinsic) and an implicit part (only
signed); the values come from EXTENSION_VALUES. An extension this
library does not know is fine if both its parts are empty, and an
UnsupportedExtension otherwise — never a
silently wrong signature.
- class robonomicsinterface.extrinsic.Call(pallet, function, args, data, scale_value, spec_version=0)[source]¶
An encoded call, ready to sign or to nest (
RWS.call,Utility.batch).
- class robonomicsinterface.extrinsic.Era(period=None, phase=0)[source]¶
How long a signed extrinsic stays valid.
An immortal extrinsic is valid until its nonce is used. A mortal one is valid for
periodblocks from the block it names, so a lost or stuck transaction cannot land much later — and cannot be replayed if the account is ever reaped and its nonce starts again from zero.
- class robonomicsinterface.extrinsic.Event(pallet, name, attributes, extrinsic_index)[source]¶
One runtime event, e.g.
Event("Datalog", "NewRecord", (...)).
- class robonomicsinterface.extrinsic.ExtrinsicResult(extrinsic_hash, block_hash, block_number, index, events, finalized)[source]¶
An extrinsic that made it into a block, and the events it produced.
- Parameters:
- class robonomicsinterface.extrinsic.SignedExtrinsic(data, hash, call, signer, nonce, era)[source]¶
A signed extrinsic, ready for
author_submitExtrinsic.
- class robonomicsinterface.extrinsic.SigningContext(nonce, era, birth_hash, genesis_hash, spec_version, transaction_version, tip=0)[source]¶
What a signature commits to besides the call: nonce, era, tip, chain.
- robonomicsinterface.extrinsic.compose_call(runtime, pallet, function, args=None)[source]¶
Encode
pallet.function(**args)for this runtime.Arguments are checked against the metadata: a missing or unknown one is an
EncodeError, as is a value that does not fit its type.
- robonomicsinterface.extrinsic.decode_events(records)[source]¶
Turn decoded
System.Eventsrecords intoEventobjects.
- robonomicsinterface.extrinsic.dispatch_error_message(runtime, dispatch_error)[source]¶
Explain a
DispatchError:(pallet, error, docs, message).
- robonomicsinterface.extrinsic.explain_invalid_transaction(raw)[source]¶
Decode a
TransactionValidity:Noneif valid, else(kind, explanation).rawis the answer ofTaggedTransactionQueue_validate_transaction.
- robonomicsinterface.extrinsic.extrinsic_hash(data)[source]¶
The hash nodes and explorers identify an extrinsic by.
- robonomicsinterface.extrinsic.sign_extrinsic(runtime, call, keypair, context)[source]¶
Sign
callwithkeypairas a version-4 extrinsic.- Parameters:
runtime (Runtime)
call (Call)
keypair (Keypair)
context (SigningContext)
- Return type:
Keys and addresses¶
ED25519 and sr25519 accounts: key derivation, signatures and message encryption.
ED25519 runs on PyNaCl, sr25519 on the [sr25519] extra (keys_sr25519).
Wire formats match substrate-interface byte for byte: the same mini secret
from a mnemonic, the same ed25519 → curve25519 conversion and the same NaCl
box with the nonce in front. Reports already encrypted by the 2.x stack stay
readable, and the ones produced here open with the old tools.
- class robonomicsinterface.keys.Keypair[source]¶
An ED25519 or sr25519 account, with or without its secret key.
Build one with a
from_*class method;crypto_typepicks the kind (ED25519by default, so an existing mnemonic keeps its address). A keypair built from a public key or an address can verify signatures and be encrypted to, but not sign or decrypt. The secret is held only inside the signing key: the mnemonic or URI it came from is not kept, and neitherrepr()nor exceptions ever show it.- classmethod from_seed(seed, ss58_format=32, *, crypto_type=0)[source]¶
From a 32-byte seed (the Substrate “mini secret”), raw or as 0x hex.
- classmethod from_mnemonic(mnemonic, password='', ss58_format=32, *, crypto_type=0)[source]¶
From a BIP39 English mnemonic, as polkadot.js and substrate-interface derive it.
- classmethod from_uri(uri, ss58_format=32, *, crypto_type=0)[source]¶
From a Substrate secret URI:
<phrase or 0x seed>//hard/soft///password.An empty phrase means the development phrase, so
//Alicegives the well-known development account. ED25519 has hard derivation only; a soft junction (/path) is refused. Use this for development and tests; for keys that people paste, usefrom_secret().
- classmethod from_secret(secret, ss58_format=32, *, crypto_type=0)[source]¶
From what a person may paste: a mnemonic or a raw 0x seed.
Derivation paths, and development keys such as
//Alicewith them, are refused on purpose: those secrets are public.
- classmethod from_json(backup, password, ss58_format=32)[source]¶
From the JSON backup a wallet exports (polkadot.js version 3) and its password.
The file says whether the account is ED25519 or sr25519, so nothing else is needed. The address in the file may be in any network’s format; the keypair gets
ss58_format. Opening it takes a few seconds on a small board: call it off an event loop.
- classmethod from_public_key(public_key, ss58_format=32, *, crypto_type=0)[source]¶
A counterparty known by its public key: can verify and be encrypted to.
- classmethod from_address(address, ss58_format=None, *, crypto_type=0)[source]¶
A counterparty known by its SS58 address.
- Parameters:
- Return type:
An address carries no key type: say which it is with
crypto_type. A wrong one only shows later, when a signature does not verify or a message does not decrypt.
- static generate_mnemonic(word_count=12)[source]¶
A new random mnemonic; build the keypair with
from_mnemonic().
- sign(message)[source]¶
A 64-byte signature. A
stris signed as its UTF-8 bytes.An sr25519 signature is randomized: signing twice gives different bytes, and both verify.
- encrypt_message(message, recipient_public_key, nonce=None)[source]¶
Encrypt for one recipient.
An ED25519 keypair makes a NaCl box with the 24-byte nonce in front; the recipient needs this keypair’s public key to open it. An sr25519 keypair makes an ECIES message (
ecies_sr25519) from a one-time key, so its own key plays no part: nothing in the message names the sender, and a public-only keypair can encrypt too.
- decrypt_message(encrypted_message, sender_public_key)[source]¶
Open what
encrypt_message()made (an ED25519 box may be from substrate-interface).sender_public_keyopens an ED25519 box; an sr25519 message carries its own one-time key, so the argument is not used.
SS58 addresses: a network prefix, a 32-byte public key and a blake2b checksum.
- robonomicsinterface.ss58.address_format(address)[source]¶
The SS58 network format an address is encoded with.
- robonomicsinterface.ss58.decode_address(address, ss58_format=None)[source]¶
Return the 32-byte public key behind an address.
- robonomicsinterface.ss58.encode_address(public_key, ss58_format=32)[source]¶
Encode a 32-byte public key (account id) as an SS58 address.
- robonomicsinterface.ss58.is_valid_address(address, ss58_format=32)[source]¶
Whether the text is a valid address of the given format (any format if
None).
BIP39 English mnemonics and the mini secret Substrate derives from them.
Substrate does not use the standard BIP39 seed. It derives a 32-byte “mini
secret” from the mnemonic’s entropy, not from the mnemonic string, with
PBKDF2-HMAC-SHA512 over 2048 rounds (substrate-bip39::seed_from_entropy).
The standard seed would give a different address from the same words, which
is why the tests pin this against vectors from substrate-interface.
Report envelope¶
The multi-recipient envelope: one symmetric key, wrapped for every recipient.
The payload is encrypted once with a random key for NaCl’s SecretBox, and that
key is encrypted separately, with Keypair.encrypt_message(), for each
address that may open it. The serialized form is the one already published by
Robonomics Report Service:
{"data": "0x<secretbox>", "keys": {"<address>": "0x<box of the key>", ...}}
Optional metadata travels inside the encrypted payload as
{"payload": <data>, "meta": {...}}; parse_decrypted() splits it.
A package has one key type, the sender’s: ED25519 senders wrap the key in NaCl boxes for ED25519 recipients, sr25519 senders in ECIES messages for sr25519 recipients. An address does not say which kind it is, so every recipient is taken to be of the sender’s kind; the recipient’s own key says how to open it.
- robonomicsinterface.envelope.decrypt_package(package, recipient, sender_address)[source]¶
Unwrap the key addressed to
recipient, then decrypt the payload.sender_addressopens an ED25519 box; an sr25519 message carries its own one-time key, so it is not used for an sr25519 recipient.- Raises:
PackageError – the package is malformed or has no key for this recipient.
PayloadError – the key or the payload does not decrypt.
- Parameters:
- Return type:
- robonomicsinterface.envelope.encrypt_for_recipients(data, sender, recipients, metadata=None)[source]¶
Encrypt
dataonce and wrap its key for each recipient address.The sender is always added as a recipient, so it can read back what it sent. Every recipient is taken to have the sender’s key type. Returns the JSON package.
Errors¶
Exceptions raised by the library.
Everything derives from RobonomicsError, so a caller can catch the
library as a whole. Input errors also derive from ValueError: they
describe bad data, and retrying them is pointless.
Messages never contain secrets: no mnemonic, seed or private key is ever formatted into an exception, including the input that failed to parse.
- exception robonomicsinterface.errors.RobonomicsError[source]¶
Bases:
ExceptionBase class for every error raised by robonomics-interface.
- exception robonomicsinterface.errors.InvalidMnemonic[source]¶
Bases:
RobonomicsError,ValueErrorThe text is not a valid BIP39 English mnemonic.
- exception robonomicsinterface.errors.InvalidAddress[source]¶
Bases:
RobonomicsError,ValueErrorThe text is not a valid SS58 address, or it has an unexpected format.
- exception robonomicsinterface.errors.InvalidKey[source]¶
Bases:
RobonomicsError,ValueErrorA seed, public key or derivation path cannot be used.
- exception robonomicsinterface.errors.NoSecretKey[source]¶
Bases:
RobonomicsErrorThe keypair holds a public key only, so it cannot sign or decrypt.
- exception robonomicsinterface.errors.DecryptionError[source]¶
Bases:
RobonomicsErrorA box does not open: wrong keys, or the ciphertext was damaged.
- exception robonomicsinterface.errors.MetadataError[source]¶
Bases:
RobonomicsErrorThe runtime metadata cannot be used, or does not describe what was asked.
- exception robonomicsinterface.errors.NoSuchPallet[source]¶
Bases:
MetadataError,LookupErrorThe runtime has no pallet with this name.
- exception robonomicsinterface.errors.NoSuchStorage[source]¶
Bases:
MetadataError,LookupErrorThe pallet has no storage item with this name.
- exception robonomicsinterface.errors.NoSuchConstant[source]¶
Bases:
MetadataError,LookupErrorThe pallet has no constant with this name.
- exception robonomicsinterface.errors.NoSuchCall[source]¶
Bases:
MetadataError,LookupErrorThe pallet has no call (extrinsic function) with this name.
- exception robonomicsinterface.errors.UnsupportedExtension[source]¶
Bases:
MetadataErrorThe runtime requires a signed extension this library cannot fill in.
- exception robonomicsinterface.errors.EncodeError[source]¶
Bases:
RobonomicsError,ValueErrorA value does not fit the type the runtime expects, e.g. a bad storage key.
- exception robonomicsinterface.errors.DecodeError[source]¶
Bases:
RobonomicsError,ValueErrorBytes from the chain do not decode as the type the metadata describes.
- exception robonomicsinterface.errors.TransportError[source]¶
Bases:
RobonomicsErrorThe node could not be reached or stopped answering. Retrying may help.
Every subclass is a network-level failure: the request may not have reached the node at all, so nothing is known about its effect.
- exception robonomicsinterface.errors.ConnectionFailed(endpoint, reason)[source]¶
Bases:
TransportErrorA connection to an endpoint could not be opened, or the node is unusable.
- exception robonomicsinterface.errors.ConnectionLost[source]¶
Bases:
TransportErrorThe connection closed while a request or subscription was in flight.
- exception robonomicsinterface.errors.RequestTimeout[source]¶
Bases:
TransportError,TimeoutErrorThe node did not answer in time.
- exception robonomicsinterface.errors.AllEndpointsFailed(failures)[source]¶
Bases:
TransportErrorNo endpoint could be used;
failuressays why for each of them.- Parameters:
failures (list[ConnectionFailed])
- Return type:
None
- exception robonomicsinterface.errors.RpcError(method, code, message, data=None)[source]¶
Bases:
RobonomicsErrorThe node answered with a JSON-RPC error. Repeating the same request will not help.
- exception robonomicsinterface.errors.TransactionError[source]¶
Bases:
RobonomicsErrorAn extrinsic was refused, failed, or its outcome is unknown.
Not retryable as such: resubmitting blindly may execute it twice.
- exception robonomicsinterface.errors.InvalidTransaction(kind, explanation)[source]¶
Bases:
TransactionErrorThe runtime refuses the extrinsic before it reaches a block.
kindis theInvalidTransaction(orUnknownTransaction) variant, e.g."Payment";explanationsays what it usually means here.
- exception robonomicsinterface.errors.ExtrinsicFailed(message, *, pallet, error, docs, result)[source]¶
Bases:
TransactionErrorThe extrinsic is in a block, and the runtime reports that the call failed.
palletanderrorname the module error (RWS,FreeWeightIsNotEnough) or areNonefor other dispatch errors;resultis theExtrinsicResult.
- exception robonomicsinterface.errors.ExtrinsicDropped(extrinsic_hash, status)[source]¶
Bases:
TransactionErrorThe node dropped the extrinsic before inclusion (dropped, invalid or usurped).
- exception robonomicsinterface.errors.ExtrinsicOutcomeUnknown(extrinsic_hash, reason)[source]¶
Bases:
TransactionErrorThe extrinsic was submitted, but whether it was included is not known.
The connection dropped or the wait timed out. It may still land: look it up by
extrinsic_hashbefore sending it again.
- exception robonomicsinterface.errors.EnvelopeError[source]¶
Bases:
RobonomicsErrorBase class for multi-recipient envelope errors.
- exception robonomicsinterface.errors.RecipientError[source]¶
Bases:
EnvelopeError,ValueErrorA recipient address cannot be used to wrap the key.
- exception robonomicsinterface.errors.PackageError[source]¶
Bases:
EnvelopeError,ValueErrorThe package is malformed, or not addressed to this recipient.
- exception robonomicsinterface.errors.PayloadError[source]¶
Bases:
EnvelopeErrorThe package is well formed but could not be decrypted.