Python
Le Python SDK dans l'espace de travail en amont est iroha-python. La première version Iroha 3 cible les surfaces actuelles Torii et Norito. Enfoncez la version du package ou la révision de source utilisée par votre intégration afin que le SDK et le nœud restent sur la même révision au format fil.
Les exemples ci-dessous à lire uniquement ont été comparés au public. Taira à https://taira.sora.org. Les exemples de mutation sont les modèles de transaction: ils nécessitent une réelle Taira autorité, clé privée, métadonnées de gaz et tous les jetons d'opérateur requis par la route cible avant leur soumission.
Utilisez les exemples dans cet ordre:
| Étapes | Cours contre le public Taira? | Ce dont vous avez besoin . |
|---|---|---|
| Appels de clients uniquement en lecture | Oui , oui . | Python colis plus accès au réseau |
| Les constructeurs locaux de signatures et d' instructions | Aucun appel au réseau avant submit() | L' extension native et votre matériel clé |
| Transactions mutantes et appels de services | Seulement avec votre propre compte financé | Compte de l'autorité, clé privée, chaîne ID, métadonnées des frais, solde des actifs des frais et jetons d'itinéraire |
| Connectez les codecs de cadre, crypto et GPU aides | Seulement locaux | L'extension native; les assistants GPU ont également besoin d'un backend capable de CUDA |
Installation
Le nom de métadonnées du paquet est iroha-python. Ne supposez pas qu'une installation non connectée PyPI correspond au réseau en direct Taira. Installez une roue ou un guichet source qui a été construit à partir de la même révision en amont vos objectifs d'intégration:
python -m pip install /path/to/iroha_python-*.whlSi votre projet consomme directement l'espace de travail en amont, installez les dépendances Python et construisez l'extension native avant d'exécuter des exemples qui utilisent Instruction, TransactionDraft, signature, cryptographie, SoraFS aides natives, GPU aides ou codecs-cadres Connect. Utilisez la commande construire à partir du flux python/iroha_python/README.md en amont, puis vérifiez que le chargement des exportations natives:
cd python/iroha_python
python - <<'PY'
from iroha_python import Instruction, generate_ed25519_keypair
print(Instruction)
print(generate_ed25519_keypair().public_key.hex())
PYSi les importations create_torii_client mais Instruction ou generate_ed25519_keypair échouent, le paquet pur Python est disponible, mais l'extension native ne l'est pas.
Démarrage rapide
Commencez par les points d'extrémité Taira publics à lecture seule:
from iroha_python import (
create_torii_client,
)
client = create_torii_client("https://taira.sora.org")
# Public reads do not need an authority or private key.
status = client.request_json("GET", "/status", expected_status=(200,))
accounts = client.list_accounts_typed(limit=5)
print(status["build"]["version"])
for account in accounts.items:
print(account.id)Configuration partagée
Utilisez cette configuration pour les modèles mutants. Remplacez chaque titulaire de place par une autorité Taira, clé privée, jeton et actif / compte IDs de votre déploiement avant de soumettre.
authority est le compte qui signe la transaction. private_key doit correspondre à ce compte, CHAIN_ID doit correspondre au réseau cible et TX_METADATA doit inclure les champs de frais attendus par le réseau. Les titulaires de postes ci-dessous sont intentionnellement invalide, ils ne sont donc pas soumis par accident.
from iroha_python import (
Ed25519KeyPair,
Instruction,
TransactionConfig,
TransactionDraft,
create_torii_client,
)
TORII_URL = "https://taira.sora.org"
CHAIN_ID = "fc56984b-2be7-431d-840e-21514d1883f0"
AUTH_TOKEN = None
# Replace these placeholders with the real signing keys for your accounts.
alice_pair = Ed25519KeyPair.from_private_key(bytes.fromhex("<alice-private-key-hex>"))
bob_pair = Ed25519KeyPair.from_private_key(bytes.fromhex("<bob-private-key-hex>"))
# The authority string must identify the same account as the private key.
alice = "<alice-account-id>"
bob = "<bob-account-id>"
ROSE_DEFINITION = "rose#wonderland"
ROSE_ASSET = "<rose-asset-id>"
BADGE_NFT = "badge$wonderland"
TX_METADATA = {
# Public Taira fee asset. Use the configured XOR asset on your network.
"gas_asset_id": "6TEAJqbb8oEPmLncoNiMRbLEK6tw",
}
client = create_torii_client(TORII_URL, auth_token=AUTH_TOKEN)
def submit(*instructions):
# This is the network boundary: build, sign, submit, and wait for status.
return client.build_and_submit_transaction(
chain_id=CHAIN_ID,
authority=alice,
private_key=alice_pair.private_key,
instructions=list(instructions),
metadata=TX_METADATA,
wait=True,
)Instruction.* n'appelle que des charges utiles d'instructions de construction. submit() est le point où le SDK signe la transaction, l'envoie à Torii et attend un statut.
Tarifs et gaz
Les transactions écrites nécessitent des métadonnées de frais et un solde d'actifs de frais financés. Sur Taira, l'actif de frais est financé par le robinet public et les métadonnées des transactions doivent inclure gas_asset_id. Sur Minamoto, les frais sont payés avec réel XOR et l'actifs ID provient de la configuration du réseau.
Les métadonnées de frais appartiennent à la transaction et non à des instructions individuelles. L'assistant submit() ci-dessus attache TX_METADATA à chaque transaction qu'il crée:
TX_METADATA = {
# Taira expects the fee asset definition in transaction metadata.
"gas_asset_id": "6TEAJqbb8oEPmLncoNiMRbLEK6tw",
}
envelope, status = client.build_and_submit_transaction(
chain_id=CHAIN_ID,
authority=alice,
private_key=alice_pair.private_key,
# Fee metadata is attached to the transaction, not the instruction.
instructions=[
Instruction.set_account_key_value(
alice,
"python_fee_example",
"ready",
)
],
metadata=TX_METADATA,
wait=True,
)Avant d'envoyer des envois, assurez-vous que le compte de l'autorité possède suffisamment d'actif sur les frais. Le robinet exact et l'actif ID sont spécifiques au réseau; voici la forme Taira:
FEE_ASSET_DEFINITION = "6TEAJqbb8oEPmLncoNiMRbLEK6tw"
# The faucet returns the concrete account asset ID to check here.
FEE_ASSET_ID = "<fee-asset-id-from-faucet-response>"
TX_METADATA = {"gas_asset_id": FEE_ASSET_DEFINITION}
# Fail before submitting if the signer cannot pay gas.
fee_assets = client.list_account_assets_typed(
alice,
limit=10,
asset_id=FEE_ASSET_ID,
)
if not fee_assets.items:
raise RuntimeError("fund the authority account with the Taira fee asset first")Le robinet retourne le béton . asset_id Il s'agit d'un ouvrage destiné à être utilisé pour la vérification du solde. gas_asset_id champ de métadonnées utilise la définition d'actif des redevances ID.
Gardez les métadonnées de l'application séparées des métadonnées des frais en fusionnant les mappings lors de la création d'une transaction:
APP_METADATA = {"source": "python-docs"}
# Merge app metadata with required fee metadata before building the draft.
metadata = {**TX_METADATA, **APP_METADATA}
draft = TransactionDraft(
TransactionConfig(
chain_id=CHAIN_ID,
authority=alice,
metadata=metadata,
)
)Si vous omettez des métadonnées de frais, utilisez le mauvais actif de frais ou signez avec un compte non financé, un réseau réel devrait rejeter la transaction même si la charge utile d'instructions est autrement valable.
Taira - Appels vérifiés en lecture seule
Ces appels ont été retournés avec succès contre le public Taira:
client = create_torii_client("https://taira.sora.org")
# Use raw requests for endpoints that do not need a typed wrapper.
status = client.request_json("GET", "/status", expected_status=(200,))
parameters = client.request_json("GET", "/v1/parameters", expected_status=(200,))
# Typed helpers parse pagination and records into dataclasses.
accounts = client.list_accounts_typed(limit=1)
domains = client.list_domains_typed(limit=1)
definitions = client.query_asset_definitions_typed(limit=1)
# These calls inspect live node subsystems without mutating state.
time_now = client.get_time_now_typed()
time_status = client.get_time_status_typed()
sumeragi = client.get_sumeragi_status_typed()
connect = client.get_connect_status_typed()
print(status["build"]["version"])
print(parameters["sumeragi"]["block_time_ms"])
print(accounts.total, domains.total, definitions.total)
print(time_now.now_ms, len(time_status.samples), sumeragi.leader_index)
print(connect.enabled, connect.sessions_active)Routes telles que /v1/status, l'inventaire public des pairs, Sumeragi RBC le prélèvement d'échantillons, les instantanés d'administration de nœud et l'administration du registre de l'application Connect n'étaient pas disponibles publiquement sur Taira lors du contrôle. request_json("GET", "/status") pour la charge utile d'état de nœud public sur Taira.
Des constructeurs d'instructions
Le SDK expose les constructeurs de type pour les familles d'instructions les plus courantes et une sortie JSON pour les variantes qui ne sont pas encore des méthodes Python de première classe. Les extraits suivants sont des modèles de transaction mutants et n'ont pas été soumis au public Taira sans compte signé.
Préférer les aides typées lorsqu'elles existent: elles normalisent les valeurs Python et échouent tôt sur les formes invalides. Utilisez Instruction.from_json uniquement lorsque vous avez besoin d'une variante d'instruction qui n'a pas encore d'aide Python.
| Famille d'instructions | La surface Python |
|---|---|
| Enregistrer | register_account, register_asset_definition_numeric, register_rwa, register_time_trigger, register_precommit_trigger; register_domain est réservé aux outils de génèse/bootstrap |
| Ne pas vous inscrire . | unregister_trigger; utilisation de Instruction.from_json pour les autres variantes |
| La menthe/le feu | mint_asset_numeric, burn_asset_numeric, mint_trigger_repetitions, burn_trigger_repetitions |
| Transfert | transfer_asset_numeric, transfer_domain, transfer_asset_definition, transfer_nft, transfer_rwa, force_transfer_rwa |
| Metadata et contrôles | set_account_key_value, remove_account_key_value, set_rwa_controls, set_rwa_key_value, remove_rwa_key_value |
| RWA cycle de vie | merge_rwas, redeem_rwa, freeze_rwa, unfreeze_rwa, hold_rwa, release_rwa |
| ExecuteTrigger | execute_trigger |
| Extensions de la répartition et du règlement | repo_initiate, repo_unwind, repo_margin_call, settlement_dvp, settlement_pvp |
| Fermetures d' actifs natifs | open_asset_lock, drawdown_asset_lock, cancel_asset_lock, expire_asset_lock, ainsi que les assistants du client *_and_wait |
| Grant/Revocate, SetParameter, Log, Custom, Upgrade et les variantes moins courantes de registre / non-registre | Instruction.from_json ou TransactionBuilder.add_instruction_json avec le canonique InstructionBox JSON |
Pour les paiements conditionnels de type escrow, voir Native Asset Escrow. Python expose actuellement des aides de première classe pour les verrouillages d'actifs génériques; le marché et les aides anonymes à l'escrow ne sont pas encore des méthodes de première classe Python.
Créer des domaines, puis enregistrer des comptes et des actifs
La création de domaine ordinaire passe par le planificateur d'alias déclaratif afin que le bail SNS, les capacités du propriétaire, la garde des devis et l'état du domaine soient vérifiés ensemble. Créez une intention non secrète AliasSetupPlanRequestV1 avec votre SDK ou votre service d'intégration, puis utilisez iroha app alias setup plan et iroha app alias setup apply. Ne soumettez pas Instruction.register_domain à partir d'une transaction d'application; ce constructeur reste utilisé pour les outils génèse/bootstrap.
Après l'engagement du plan de configuration de domaine, enregistrez des objets appartenant à un domaine. Sur un réseau partagé tel que Taira, utilisez un espace de noms de domaine et de compte qui vous est attribué.
# The domain and its SNS lease already exist before this transaction.
submit(
Instruction.register_account(alice, {"display_name": "Alice"}),
Instruction.register_account(bob, {"display_name": "Bob"}),
Instruction.register_asset_definition_numeric(
ROSE_DEFINITION,
owner=alice,
scale=2,
mintable="Infinitely",
confidential_policy="TransparentOnly",
metadata={"symbol": "ROS"},
),
)mintable accepte Infinitely, Once, Not, ou Limited(n) les valeurs acceptées par le modèle de données. scale pour un actif numérique sans contrainte.
La menthe, la combustion et le transfert des biens
Ces appels utilisent un actif existant ID. Enregistrez d'abord la définition de l'actif, puis construisez le actif concret ID pour le compte qui détient l'actifs.
# Increase the account's asset balance.
submit(Instruction.mint_asset_numeric(ROSE_ASSET, "100.00"))
# Move part of the balance to another account.
submit(Instruction.transfer_asset_numeric(ROSE_ASSET, "25.50", bob))
# Decrease the remaining balance.
submit(Instruction.burn_asset_numeric(ROSE_ASSET, "10.00"))Transfert de propriété
Les transferts de propriété changent qui contrôle le domaine, la définition des actifs ou NFT. Utilisez le propriétaire actuel comme autorité de transaction.
# The first argument is the current owner; the last is the new owner.
submit(Instruction.transfer_domain(alice, "wonderland", bob))
submit(Instruction.transfer_asset_definition(alice, ROSE_DEFINITION, bob))
submit(Instruction.transfer_nft(alice, BADGE_NFT, bob))Configuration et suppression des métadonnées
Les valeurs de métadonnées doivent être sérialisables JSON. Lorsque vous utilisez TransactionDraft, l'autorité dans TransactionConfig devient le compte cible par défaut.
# Values are encoded as JSON metadata under the target account.
submit(
Instruction.set_account_key_value(
alice,
"profile",
{"display_name": "Alice", "tier": "operator"},
)
)
# Removing the key deletes the metadata entry from the account.
submit(Instruction.remove_account_key_value(alice, "profile"))Le projet d'aide de haut niveau vise par défaut l'autorité chargée des opérations:
draft = TransactionDraft(
TransactionConfig(chain_id=CHAIN_ID, authority=alice, metadata=TX_METADATA)
)
# With a draft, account metadata methods default to the draft authority.
draft.set_account_key_value("nickname", "Queen Alice")
draft.remove_account_key_value("nickname")Actifs du monde réel
RWA les aides utilisent JSON- des charges utiles sérialisables pour les métadonnées, la provenance et la politique du contrôleur spécifiques à l'actif. register_rwa n'accepte pas un id ou owner: le temps d'exécution génère les RwaId, et l'autorité de transaction devient le propriétaire initial.
draft = TransactionDraft(
TransactionConfig(chain_id=CHAIN_ID, authority=alice, metadata=TX_METADATA)
)
# Register the lot in a domain. Store business identifiers in primary_reference
# or metadata, then query the generated RWA ID after the transaction commits.
draft.register_rwa(
{
"domain": "commodities.universal",
"quantity": "100",
"spec": {"scale": 0},
"primary_reference": "warehouse-receipt-001",
"status": "active",
"metadata": {
"commodity": "copper",
"warehouse": "DXB-01",
},
"parents": [],
"controls": {
"controller_accounts": [alice],
"controller_roles": [],
"freeze_enabled": True,
"hold_enabled": True,
"force_transfer_enabled": True,
"redeem_enabled": True,
},
}
)Après l'engagement de la transaction d'enregistrement, utilisez FindRwas, /v1/rwas, un événement RWA ou le chemin d'explorateur réglé pour découvrir le ID généré:
page = client.list_rwas_typed(limit=20, offset=0)
for lot in page.items:
print(lot.id)Les opérations suivantes utilisent le hash$domain ID généré:
registered_rwa_id = (
"0123456789abcdef0123456789abcdef"
"0123456789abcdef0123456789abcdef$commodities.universal"
)
draft = TransactionDraft(
TransactionConfig(chain_id=CHAIN_ID, authority=alice, metadata=TX_METADATA)
)
# Transfer, hold, release, freeze, and redeem model the lot lifecycle.
draft.transfer_rwa(
registered_rwa_id,
quantity="10",
destination=bob,
)
draft.hold_rwa(registered_rwa_id, quantity="5")
draft.release_rwa(registered_rwa_id, quantity="5")
draft.freeze_rwa(registered_rwa_id)
draft.unfreeze_rwa(registered_rwa_id)
draft.redeem_rwa(registered_rwa_id, quantity="1")
# RWA metadata and controls are separate from account metadata.
draft.set_rwa_key_value(registered_rwa_id, "auditor", "alice")
draft.remove_rwa_key_value(registered_rwa_id, "auditor")
draft.set_rwa_controls(
registered_rwa_id,
{
"controller_accounts": [alice],
"controller_roles": [],
"freeze_enabled": True,
"hold_enabled": True,
"force_transfer_enabled": True,
"redeem_enabled": True,
},
)
# Merge consumes quantities from parent lots with the same domain and spec. The
# child lot gets a generated ID.
draft.merge_rwas(
{
"parents": [
{"rwa": registered_rwa_id, "quantity": "40"},
{
"rwa": "fedcba9876543210fedcba9876543210"
"fedcba9876543210fedcba9876543210$commodities.universal",
"quantity": "60",
},
],
"primary_reference": "warehouse-receipt-003",
"status": "merged",
"metadata": {"merge_reason": "same custodian and quality grade"},
}
)
# Force transfer requires a configured controller and force_transfer_enabled.
draft.force_transfer_rwa(
registered_rwa_id,
quantity="1",
destination=bob,
)Les transferts complets peuvent modifier owned_by sur le lot existant. Les transferts et les fusions partiels créent des lots d'enfants générés.
Les déclencheurs
Utilisez les aides d' enregistrement de déclencheur lorsque l' exécutable est une autre séquence d' instructions:
# The trigger executable is just another instruction payload.
reward = Instruction.mint_asset_numeric(ROSE_ASSET, "1")
# Time triggers run on a schedule once registered.
register_hourly = Instruction.register_time_trigger(
"hourly_reward",
alice,
[reward],
start_ms=1_800_000_000_000,
period_ms=3_600_000,
repeats=24,
metadata={"purpose": "docs"},
)
submit(register_hourly)
# Precommit triggers run during the transaction pipeline.
register_precommit = Instruction.register_precommit_trigger(
"precommit_reward",
alice,
[reward],
repeats=10,
metadata={"purpose": "pipeline test"},
)
submit(register_precommit)
# Trigger execution and repetition changes are also transactions.
submit(Instruction.execute_trigger("hourly_reward", args={"reason": "manual"}))
submit(Instruction.mint_trigger_repetitions("hourly_reward", 5))
submit(Instruction.burn_trigger_repetitions("hourly_reward", 1))
submit(Instruction.unregister_trigger("hourly_reward"))Torii expose également les aides de REST pour l'inventaire des déclencheurs:
# Inventory helpers are reads; they do not unregister or execute triggers.
registered = client.list_triggers_typed(limit=20)
for trigger in registered.items:
print(trigger.id, trigger.authority)
details = client.get_trigger_typed("precommit_reward")Les appels d'inventaire des déclencheurs ne sont effectués qu'après lecture ou inspection des enregistrements de déclenchement. L'enregistrement, l'exécution, la répétition des modifications et le non-enregistrement sont des opérations mutantes.
Instructions en matière de rétablissement et de règlement
Repo et aides à la réglementation bilatérale ajoutent des variantes d'instructions spécifiques au domaine sans charge utile de fabrication manuelle Norito:
from iroha_python import (
RepoCashLeg,
RepoCollateralLeg,
RepoGovernance,
SettlementAtomicity,
SettlementExecutionOrder,
SettlementLeg,
SettlementPlan,
)
config = TransactionConfig(
chain_id=CHAIN_ID,
authority=alice,
# Keep repo and settlement examples bounded by a short TTL.
ttl_ms=120_000,
metadata=TX_METADATA,
)
draft = TransactionDraft(config)
# Each repo leg describes one side of the financing agreement.
cash = RepoCashLeg(asset_definition_id="usd#wonderland", quantity="1000")
collateral = RepoCollateralLeg(
asset_definition_id="bond#wonderland",
quantity="1050",
metadata={"isin": "ABC123"},
)
governance = RepoGovernance(haircut_bps=1500, margin_frequency_secs=86_400)
# Domain-specific draft methods append the corresponding instructions.
draft.repo_initiate(
agreement_id="daily_repo",
initiator=alice,
counterparty=bob,
cash_leg=cash,
collateral_leg=collateral,
rate_bps=250,
maturity_timestamp_ms=1_704_000_000_000,
governance=governance,
)
draft.repo_margin_call("daily_repo")
draft.repo_unwind(
agreement_id="daily_repo",
initiator=alice,
counterparty=bob,
cash_leg=cash,
collateral_leg=collateral,
settlement_timestamp_ms=1_704_086_400_000,
)
# DVP/PVP settlement plans encode ordering and atomicity for both legs.
delivery = SettlementLeg(
asset_definition_id="bond#wonderland",
quantity="10",
from_account=alice,
to_account=bob,
metadata={"isin": "ABC123"},
)
payment = SettlementLeg(
asset_definition_id="usd#wonderland",
quantity="1000",
from_account=bob,
to_account=alice,
)
plan = SettlementPlan(
order=SettlementExecutionOrder.PAYMENT_THEN_DELIVERY,
atomicity=SettlementAtomicity.ALL_OR_NOTHING,
)
draft.settlement_dvp(
settlement_id="trade_dvp",
delivery_leg=delivery,
payment_leg=payment,
plan=plan,
metadata={"desk": "rates"},
)
draft.settlement_pvp(
settlement_id="trade_pvp",
primary_leg=payment,
counter_leg=delivery,
)
envelope = draft.sign_with_keypair(alice_pair)
client.submit_transaction_envelope_and_wait(envelope)JSON Escape Hatch
Quand une Python l'aide n'est pas encore disponible, fournissez un modèle de données canonique InstructionBox JSON dans Instruction.from_json ou directement dans TransactionBuilder.add_instruction_json. C' est la voie recommandée pour Grant, Revoke, SetParameter, Log, Custom, Upgrade, Peer/rôle NFT l'enregistrement, et les variantes non déclencheuses de non-enregistrement jusqu'à ce que ces aides soient tapées.
from iroha_python import Instruction, TransactionBuilder
# Copy this payload from Rust/CLI tooling or from a pinned data-model schema.
instruction_box_json = """
{
"<InstructionVariant>": {
"...": "..."
}
}
"""
instruction = Instruction.from_json(instruction_box_json)
submit(instruction)
# Use TransactionBuilder when you need lower-level control than TransactionDraft.
builder = TransactionBuilder(CHAIN_ID, alice)
builder.set_metadata(TX_METADATA)
builder.add_instruction_json(instruction_box_json)
envelope = builder.sign(alice_pair.private_key)
client.submit_transaction_envelope_and_wait(envelope)Pour les instructions générées ou opaques, aller-retour à travers JSON avant le stockage des appareils:
# Round trips are useful for validating fixtures generated by another tool.
payload = Instruction.mint_asset_numeric(ROSE_ASSET, "1").to_json()
same_instruction = Instruction.from_json(payload)
print(same_instruction.as_dict())Les flux de travail des transactions
Utilisez TransactionDraft pour les applications qui construisent plusieurs instructions avant de signer. Un projet vous permet de garder des paramètres au niveau de la transaction tels que ttl_ms, nonce et des métadonnées en un seul endroit, puis signez une fois:
config = TransactionConfig(
chain_id=CHAIN_ID,
authority=alice,
# TTL and nonce are transaction-level properties shared by all instructions.
ttl_ms=120_000,
nonce=1,
metadata={**TX_METADATA, "source": "python-docs"},
)
draft = TransactionDraft(config)
# Draft methods append instructions but do not submit anything yet. Domain
# setup is a separate alias-planner flow and has already committed here.
draft.register_account(bob, metadata={"role": "user"})
draft.register_asset_definition_numeric(
ROSE_DEFINITION,
owner=alice,
scale=2,
mintable="Infinitely",
)
draft.mint_asset_numeric(ROSE_ASSET, "100")
draft.transfer_asset_numeric(ROSE_ASSET, "25", destination=bob)
# Signing freezes the draft into an envelope ready for Torii.
envelope = draft.sign_with_keypair(alice_pair)
receipt = client.submit_transaction_envelope(envelope)
status = client.wait_for_transaction_status(envelope.hash_hex(), timeout=30)
print(receipt, status)Exporter un manifeste déterministique à l'examen, à l'audit ou au transfert de portefeuille:
import json
from pathlib import Path
# Manifests are review artifacts; they are not submitted by themselves.
manifest = draft.to_manifest_dict(include_creation_time=True)
print(json.dumps(manifest, indent=2))
Path("transaction_manifest.json").write_text(
draft.to_manifest_json(indent=2, include_creation_time=True),
encoding="utf-8",
)Ajouter une preuve de confidentialité avant la signature lorsque la voie cible l'exige:
# Attach the proof before signing so it is covered by the transaction hash.
draft.add_lane_privacy_merkle_proof(
commitment_id=7,
leaf=bytes.fromhex("aa" * 32),
leaf_index=3,
audit_path=[bytes.fromhex("bb" * 32), None, bytes.fromhex("cc" * 32)],
proof_backend="halo2/ipa",
proof_bytes=b"...proof bytes...",
verifying_key_bytes=b"...verifying key bytes...",
)
envelope = draft.sign_with_keypair(alice_pair)Questions posées
Les assistants de requête typés renvoient des classes de données au lieu des dictionnaires JSON bruts. Ils sont le moyen le plus simple de commencer parce que le SDK analyse la pagination et les champs d'enregistrement communs pour vous:
# Typed pages expose `.items` plus pagination metadata such as `.total`.
accounts = client.list_accounts_typed(limit=25, sort="id")
for account in accounts.items:
print(account.id, account.metadata)
domains = client.list_domains_typed(limit=10)
definitions = client.query_asset_definitions_typed(limit=10)
print(domains.total, definitions.total)Utilisez les aides à la demande génériques lorsqu'un point final Torii ne dispose pas encore d'une enveloppe typée:
# Drop to raw JSON when you need an endpoint before a typed helper exists.
payload = client.request_json("GET", "/v1/parameters", expected_status=(200,))
metrics = client.get_metrics(as_text=True)Les aides à l'inventaire des comptes ont besoin d'un identifiant de compte accepté par le SDK C' est un normalisateur. I105 compte IDs ou des pseudonymes en chaîne; si un explorateur de blocs ou un point final brut renvoie une ID que le SDK rejette, résoudre à un compte canonique ID avant d'appeler ces aides:
# These helpers expect a canonical account ID or an alias the SDK can normalize.
assets = client.list_account_assets_typed(alice, limit=10)
transactions = client.query_account_transactions_typed(alice, limit=5)
permissions = client.list_account_permissions_typed(alice, limit=20)
print(len(assets.items), len(transactions.items), len(permissions.items))Les événements
Décodage des aides de diffusion JSON les charges utiles par défaut. with_metadata=True quand vous avez besoin du SSE Le nom de l'événement, l'identifiant, l'indice et la charge utile crue. EventCursor Ces exemples attendent les événements en direct, alors les faire entrer contre un nœud où le flux d'événements correspondant est activé et activé.
from iroha_python import DataEventFilter, EventCursor
# Narrow the stream to proof events with the expected backend and proof hash.
proof_filter = DataEventFilter.proof(
backend="halo2/ipa",
proof_hash_hex="deadbeef" * 8,
)
# Persist the latest SSE id so a reconnect can resume from the same point.
cursor = EventCursor()
for event in client.stream_events(
filter=proof_filter,
cursor=cursor,
resume=True,
with_metadata=True,
):
print(event.id, event.event, event.data)
break
for event in client.stream_trigger_events(trigger_id="hourly_reward", resume=True):
print(event)
break
for tx_event in client.stream_pipeline_transactions(status="Queued"):
print(tx_event)
breakLes clés et les adresses
Le SDK expose les aides de signature locales pour chaque algorithme de signature compilé dans l'extension native. Ces aides n'appellent pas Taira, mais elles nécessitent l'expansion native:
from iroha_python import (
ED25519_ALGORITHM,
derive_confidential_keyset_from_hex,
derive_keypair_from_seed,
hash_blake2b_32,
verify,
)
from iroha_python.address import AccountAddress
# Key derivation and signing are local; no network call is made here.
ed_pair = derive_keypair_from_seed(b"alice", ED25519_ALGORITHM)
signature = ed_pair.sign(b"payload")
assert verify(ED25519_ALGORITHM, ed_pair.public_key, b"payload", signature)
# Canonical AccountId/I105 identity is derived only from the controller key.
# This constructor currently requires `domain`; canonical identity ignores it
# and AccountAddress.from_account emits a domainless address.
address = AccountAddress.from_account(domain="wonderland", public_key=ed_pair.public_key)
print(address.canonical_hex())
print(address.to_i105(0x02F1))
# Confidential key helpers derive local viewing/spending material.
confidential = derive_confidential_keyset_from_hex("01" * 32)
print(confidential.as_hex())
print(hash_blake2b_32(b"payload").hex())Utilisez supported_crypto_algorithms() pour voir ce que votre roue prend en charge. Les aides génériques utilisent des étiquettes d'algorithmes canoniques et fonctionnent pour Ed25519, secp256k1, ML-DSA, GOST, BLS et SM2 quand ces algorithmes sont compilés dans:
from iroha_python import (
CryptoKeyPair,
derive_keypair_from_seed,
load_keypair,
parse_private_key_multihash,
parse_public_key_multihash,
private_key_multihash,
public_key_multihash,
sign,
supported_crypto_algorithms,
verify,
)
message = b"iroha multi-algorithm signing"
# Iterate the algorithms compiled into the installed native extension.
for algorithm in supported_crypto_algorithms():
keypair = derive_keypair_from_seed(f"docs:{algorithm}".encode(), algorithm)
signature = keypair.sign(message)
# Both the object method and the generic helper verify the same signature.
assert keypair.verify(message, signature)
assert verify(algorithm, keypair.public_key, message, signature)
# Loading a private key should reconstruct the same public key.
loaded = load_keypair(keypair.private_key, algorithm)
assert loaded.public_key == keypair.public_key
assert sign(algorithm, loaded.private_key, message) != b""
# Prefixed multihashes carry the algorithm label with the key bytes.
public_multihash = public_key_multihash(
algorithm,
keypair.public_key,
prefixed=True,
)
private_multihash = private_key_multihash(
algorithm,
keypair.private_key,
prefixed=True,
)
public_algorithm, public_key = parse_public_key_multihash(public_multihash)
private_algorithm, private_key = parse_private_key_multihash(private_multihash)
restored = CryptoKeyPair.from_private_key_multihash(private_multihash)
# Round-trip checks catch mismatched algorithm labels or key encodings.
assert public_algorithm == algorithm
assert public_key == keypair.public_key
assert private_algorithm == algorithm
assert private_key == keypair.private_key
assert restored == keypairLa cryptographie en chinois SM
Le Python SDK expose à la fois les aides génériques SM2 et les aides de commodité spécifiques SM2. Utilisez l'annonce de capacité du nœud pour sélectionner l'identifiant distinctif SM2 attendu par le réseau cible:
from iroha_python import (
SM2_ALGORITHM,
SM2_DEFAULT_DISTINGUISHED_ID,
derive_keypair_from_seed,
derive_sm2_keypair_from_seed,
sign,
sign_sm2,
verify,
verify_sm2,
)
capabilities = client.get_node_capabilities_typed()
sm = capabilities.crypto.sm if capabilities.crypto else None
# Use the node's default SM2 distinguishing ID when the node advertises one.
distid = sm.sm2_distid_default if sm else SM2_DEFAULT_DISTINGUISHED_ID
# The SM2-specific helper accepts the distinguishing ID explicitly.
pair = derive_sm2_keypair_from_seed(bytes.fromhex("11" * 32), distid=distid)
message = b"iroha-sm2-example"
signature = pair.sign(message)
assert pair.verify(message, signature)
assert verify_sm2(pair.public_key, message, signature, distid=distid)
assert sign_sm2(pair.private_key, message, distid=distid) != b""
# The generic API works when you only need the canonical `sm2` label.
generic_pair = derive_keypair_from_seed(bytes.fromhex("22" * 32), SM2_ALGORITHM)
generic_signature = sign(SM2_ALGORITHM, generic_pair.private_key, message)
assert verify(SM2_ALGORITHM, generic_pair.public_key, message, generic_signature)
print(pair.public_key_sec1_hex)
print(pair.public_key_multihash)crypto.sm.enabled vous indique si le nœud accepte les algorithmes de la famille SM dans sa politique actuelle. La même annonce inclut la politique de hachage et l'état d'accélération SM, ce qui est utile pour décider s'il faut ou non activer des flux spécifiques à SM2.
capabilities = client.get_node_capabilities_typed()
# `enabled` is the submit-time policy flag, not just local SDK support.
if capabilities.crypto and capabilities.crypto.sm.enabled:
sm = capabilities.crypto.sm
print(sm.default_hash)
print(sm.allowed_signing)
print(sm.acceleration.policy)
else:
print("SM crypto is not enabled by this node")Le public Taira a exposé l'annonce de capacité SM pendant le contrôle, mais la signature SM y a été désactivée. Ses algorithmes de signature annoncés étaient ed25519, secp256k1 et bls_normal, ne soumettent pas de transactions signées SM2 à ce déploiement, sauf si la charge utile des capacités change.
GOST et les clés post-quantiques
Utilisez la crypto générique API pour GOST R 34.10-2012 ensembles de paramètres et ML-DSA (ml-dsaLe même objet de paire de clés gère la signature, la vérification et l'exportation multi-hash:
from iroha_python import (
GOST_3410_2012_256_PARAMSET_A_ALGORITHM,
GOST_3410_2012_256_PARAMSET_B_ALGORITHM,
GOST_3410_2012_256_PARAMSET_C_ALGORITHM,
GOST_3410_2012_512_PARAMSET_A_ALGORITHM,
GOST_3410_2012_512_PARAMSET_B_ALGORITHM,
ML_DSA_ALGORITHM,
derive_keypair_from_seed,
verify,
)
from iroha_python.address import AccountAddress
CHAIN_DISCRIMINANT = 0x02F1
message = b"iroha gost and post-quantum example"
# Crypto helpers use canonical labels; account addresses use compact aliases.
# Every `domain=` argument below is ignored when the canonical AccountId/I105
# address is encoded.
GOST_ADDRESS_ALIASES = {
GOST_3410_2012_256_PARAMSET_A_ALGORITHM: "gost-256-a",
GOST_3410_2012_256_PARAMSET_B_ALGORITHM: "gost-256-b",
GOST_3410_2012_256_PARAMSET_C_ALGORITHM: "gost-256-c",
GOST_3410_2012_512_PARAMSET_A_ALGORITHM: "gost-512-a",
GOST_3410_2012_512_PARAMSET_B_ALGORITHM: "gost-512-b",
}
# Derive and verify one local keypair for every GOST parameter set.
for crypto_algorithm, address_algorithm in GOST_ADDRESS_ALIASES.items():
keypair = derive_keypair_from_seed(
f"docs:{crypto_algorithm}".encode(),
crypto_algorithm,
)
signature = keypair.sign(message)
assert verify(crypto_algorithm, keypair.public_key, message, signature)
address = AccountAddress.from_account(
domain="wonderland",
public_key=keypair.public_key,
# Account addresses use compact curve aliases for GOST parameter sets.
algorithm=address_algorithm,
)
print(crypto_algorithm)
print(address.canonical_hex())
print(address.to_i105(CHAIN_DISCRIMINANT))
print(keypair.prefixed_public_key_multihash)
# ML-DSA follows the same generic signing and address flow.
mldsa_keypair = derive_keypair_from_seed(b"docs:ml-dsa", ML_DSA_ALGORITHM)
mldsa_signature = mldsa_keypair.sign(message)
assert verify(ML_DSA_ALGORITHM, mldsa_keypair.public_key, message, mldsa_signature)
post_quantum_address = AccountAddress.from_account(
domain="wonderland",
public_key=mldsa_keypair.public_key,
algorithm="ml-dsa",
)
print(post_quantum_address.canonical_hex())
print(post_quantum_address.to_i105(CHAIN_DISCRIMINANT))
print(mldsa_keypair.prefixed_public_key_multihash)Portée GOST et flux post-quantum sur les algorithmes de signature annoncés du nœud. Utilisez la charge utile des capacités brutes pour les noms d'algorithmes compatibles avec l'avenir:
capabilities = client.request_json(
"GET",
"/v1/node/capabilities",
expected_status=(200,),
)
crypto = capabilities.get("crypto", {})
sm = crypto.get("sm", {})
# Nodes advertise the signing algorithms they will accept for transactions.
allowed = set(sm.get("allowed_signing", []))
GOST_ALGORITHMS = {
"gost3410-2012-256-paramset-a",
"gost3410-2012-256-paramset-b",
"gost3410-2012-256-paramset-c",
"gost3410-2012-512-paramset-a",
"gost3410-2012-512-paramset-b",
}
# Local support is not enough; submit only when the node advertises support.
supports_gost = bool(allowed & GOST_ALGORITHMS)
supports_post_quantum = "ml-dsa" in allowed
supports_sm2 = "sm2" in allowed and bool(sm.get("enabled", False))
print(supports_gost, supports_post_quantum, supports_sm2)Si un nœud ne publie pas l'algorithme dont vous avez besoin, utilisez la clé uniquement pour les flux de travail locaux ou hors ligne. Ne soumettez pas les transactions signées avec cet algorithme à ce nœud Au cours de la vérification publique Taira, GOST et ML-DSA étaient disponibles en tant qu'assistants cryptographiques SDK dans la bibliothèque Python en amont, mais n'avaient pas été annoncés par le nœud pour la signature des transactions.
Création d'un client conscient de la configuration
Utilisez resolve_torii_client_config lorsque votre application lit les paramètres de nœud d'un fichier, mais a encore besoin de suppressions spécifiques à l'environnement ou aux tests:
import json
from iroha_python import create_torii_client, resolve_torii_client_config
with open("iroha_config.json", "r", encoding="utf-8") as handle:
raw_config = json.load(handle)
# Override only the fields that vary by environment.
resolved = resolve_torii_client_config(
config=raw_config,
overrides={"timeout_ms": 2_000, "max_retries": 5},
)
# Pass the resolved config into the same client constructor used elsewhere.
client = create_torii_client(
raw_config.get("torii", {}).get("address", TORII_URL),
resolved_config=resolved,
)Les préparatifs de Kagemusha
Les États membres Python SDK peut consulter le courant JSON la route de préparation à travers son générique Torii auxiliaire de demande:
ASSET_DEFINITION_ID = "<canonical_asset_definition_id>"
readiness = client.request_json(
"GET",
"/v1/offline/readiness",
params={"asset_definition_id": ASSET_DEFINITION_ID},
headers={"Accept": "application/json"},
expected_status=(200,),
)
print(readiness["ready"])
print(readiness["blockers"])Python n'expose pas les constructeurs d'archives de remplissage ou de rédemption de Kagemusha typés. Utilisez un portefeuille Swift ou JVM typé pour créer les archives canoniques V4, puis soumettez-les et enquêtez-les via un client Kagemusha Torii pris en charge.
Les abonnements
Les aides à l'abonnement sont des appels de service mutants hérités du client partagé Torii utilisé par iroha_python.ToriiClient. Utilisez IDs et les actifs existant sur le réseau que vous ciblez.
# The plan defines billing cadence, retry policy, and usage pricing.
usage_plan = {
"provider": alice,
"billing": {
"cadence": {
"kind": "monthly_calendar",
"detail": {"anchor_day": 1, "anchor_time_ms": 0},
},
"bill_for": {"period": "previous_period", "value": None},
"retry_backoff_ms": 86_400_000,
"max_failures": 3,
"grace_ms": 604_800_000,
},
"pricing": {
"kind": "usage",
"detail": {
"unit_price": "0.024",
"unit_key": "compute_ms",
"asset_definition": "usd#wonderland",
},
},
}
# The provider signs plan creation.
client.create_subscription_plan(
authority=alice,
private_key=alice_pair.private_key_hex,
plan_id="compute#wonderland",
plan=usage_plan,
)
# The subscriber signs subscription creation.
client.create_subscription(
authority=bob,
private_key=bob_pair.private_key_hex,
subscription_id="sub-001",
plan_id="compute#wonderland",
)
# Usage is recorded by the provider and then charged on demand.
client.record_subscription_usage(
"sub-001",
authority=alice,
private_key=alice_pair.private_key_hex,
unit_key="compute_ms",
delta="3600000",
)
client.charge_subscription_now(
"sub-001",
authority=alice,
private_key=alice_pair.private_key_hex,
)Connectez
Construire et analyser Connect URIs, et lire l'état public de la connexion exposé par Taira:
from iroha_python.connect import ConnectUri, build_connect_uri, parse_connect_uri
# Connect URIs are what an app hands to a wallet to start a session.
uri = build_connect_uri(
ConnectUri(
sid="base64url-session-id",
chain_id=CHAIN_ID,
node="taira.sora.org",
)
)
parsed = parse_connect_uri(uri)
# Status tells you whether the node currently exposes Connect.
status = client.get_connect_status_typed()
assert parsed.chain_id == CHAIN_ID
print(status.enabled, status.sessions_active)Les codecs de cadre, la dérivation des clés de session et la création de sessions nécessitent l'extension native et un itinéraire de session Connect activé:
from iroha_python import (
ConnectControlClose,
ConnectControlOpen,
ConnectDirection,
ConnectFrame,
ConnectPermissions,
decode_connect_frame,
encode_connect_frame,
generate_connect_keypair,
)
# The app keypair is separate from the account key used for transactions.
connect_pair = generate_connect_keypair()
info = client.create_connect_session_info(
{"role": "app", "sid": connect_pair.public_key.hex()}
)
print(info.app_uri, info.wallet_token, info.expires_at)
# Control frames negotiate permissions before encrypted messages are sent.
frame = ConnectFrame(
sid=bytes.fromhex("01" * 32),
direction=ConnectDirection.APP_TO_WALLET,
sequence=1,
control=ConnectControlOpen(
app_public_key=connect_pair.public_key,
chain_id=CHAIN_ID,
permissions=ConnectPermissions(methods=["SIGN_REQUEST_TX"], events=[]),
),
)
payload = encode_connect_frame(frame)
assert decode_connect_frame(payload) == frame
# Closing the control channel is explicit and carries a reason code.
client.send_connect_control_frame(
"base64url-session-id",
ConnectControlClose(role="App", code=4100, reason="finished", retryable=False),
)Encrivez les messages post-approbation avec une session d' état:
from iroha_python import (
ConnectDirection,
ConnectSession,
ConnectSessionKeys,
ConnectSignRequestRawPayload,
)
# Derive symmetric session keys from both parties' keys and the session ID.
keys = ConnectSessionKeys.derive(
local_private_key=bytes.fromhex("11" * 32),
peer_public_key=bytes.fromhex("22" * 32),
sid=bytes.fromhex("33" * 32),
)
session = ConnectSession(
sid=bytes.fromhex("33" * 32),
keys=keys,
)
# Encrypt application payloads after the session is approved.
encrypted = session.encrypt_app_to_wallet(
ConnectSignRequestRawPayload(domain_tag="SIGN", payload=b"hash")
)
state = session.snapshot_state().to_dict()
print(encrypted.sequence, state)La gouvernance, le temps d'exécution et les surfaces de gestion
Ces appels en lecture seulement ont été retournés avec succès contre le public Taira:
client = create_torii_client("https://taira.sora.org")
# Governance reads return either current settings or typed not-found wrappers.
protected = client.get_protected_namespaces()
referendum = client.get_governance_referendum_typed("ref-1")
tally = client.get_governance_tally_typed("ref-1")
locks = client.get_governance_locks_typed("ref-1")
unlock_stats = client.get_governance_unlock_stats_typed()
print(protected, referendum.found)
print(tally.approve, list(locks.locks), unlock_stats.expired_locks_now)
# Runtime reads expose the active ABI and any pending upgrade records.
abi = client.get_runtime_abi_active_typed()
abi_hash = client.get_runtime_abi_hash_typed()
runtime_metrics = client.get_runtime_metrics_typed()
upgrades = client.list_runtime_upgrades_typed()
capabilities = client.get_node_capabilities_typed()
print(abi, abi_hash, runtime_metrics)
print(upgrades.total, capabilities.abi_version)Les aides à la mise à niveau du temps d'exécution acceptent la forme manifeste utilisée par la mise à jour du temps de fonctionnement API. Ce sont des actions de l'opérateur, alors utilisez-les uniquement contre un nœud où votre compte et votre jeton sont autorisés:
admin = create_torii_client(
TORII_URL,
auth_token="admin-token",
api_token="torii-token",
)
# Propose creates the upgrade instructions; activation/cancel are operator actions.
upgrade = admin.propose_runtime_upgrade(
{
"name": "Refresh runtime provenance",
"description": "Schedules a no-ABI-change runtime rollout.",
"abi_version": 1,
"abi_hash": "00" * 32,
"added_syscalls": [],
"added_pointer_types": [],
"start_height": 1_500_000,
"end_height": 1_500_256,
}
)
print(upgrade["tx_instructions"])
admin.activate_runtime_upgrade("deadbeef" * 4)
admin.cancel_runtime_upgrade("feedface" * 4)Statut, consensus et télémétrie du réseau
# `/status` is the public node snapshot endpoint on Taira.
status = client.request_json("GET", "/status", expected_status=(200,))
print(status["blocks"], status["txs_approved"])
# Sumeragi and time endpoints expose consensus and clock diagnostics.
sumeragi = client.get_sumeragi_status_typed()
print(sumeragi.highest_qc.height, sumeragi.tx_queue.saturated)
time_now = client.get_time_now_typed()
time_status = client.get_time_status_typed()
for sample in time_status.samples:
print(sample.peer, sample.last_offset_ms, sample.last_rtt_ms)
print(time_now.now_ms)SoraFS, UAID et Kaigi Les aides
Ces aides sont disponibles lorsque le nœud cible expose la correspondante Nexus/SORA les points d'expiration. traiter des listes vides comme une réponse valide: public Taira peut avoir l'itinéraire activé sans données pour le manifeste d'échantillonnage, ou UAID.
# SoraFS status queries are reads scoped by manifest and status.
por_status = client.get_sorafs_por_status(manifest_hex="ab" * 32, status="verified")
print(len(por_status))
# UAID helpers inspect wallet/data-space bindings for one identifier.
uaid = "aabb" * 16
bindings = client.get_uaid_bindings_typed(uaid)
manifests = client.list_space_directory_manifests_typed(
uaid,
dataspace=11,
status="active",
)
print(len(bindings.dataspaces), len(manifests.manifests))
# Kaigi health summarizes relay availability when the route is enabled.
health = client.get_kaigi_relays_health_typed()
print(health.healthy_total, health.failovers_total)Norito RPC et GPU Les aides
Utilisation NoritoRpcClient quand vous avez déjà Norito octets et besoin d'appeler un binaire Torii point de fin. L'exemple nécessite une enveloppe signée à partir d'un modèle de transaction précédent:
from iroha_python import NoritoRpcClient, NoritoRpcConfig
# Use the binary RPC client for endpoints that expect Norito bytes.
with NoritoRpcClient(NoritoRpcConfig(TORII_URL, timeout=5.0)) as rpc:
response_bytes = rpc.call("/v1/transaction", envelope.signed_transaction_versioned)
print(len(response_bytes))Les aides CUDA retournent None lorsque le backend n'est pas disponible, de sorte que les applications peuvent se retrouver dans des implémentations scalaires:
from iroha_python import bn254_add_cuda, cuda_available, poseidon2_cuda
# Always probe CUDA availability before calling optional GPU helpers.
if cuda_available():
print(poseidon2_cuda(1, 2))
print(bn254_add_cuda((1, 0, 0, 0), (2, 0, 0, 0)))Couverture actuelle
Le Python SDK comprend déjà des aides pour:
- Torii flux de soumission, d'état, de requête et d'administration
- constructeurs d'instructions de type pour les extensions communes ISI et spécifiques à un domaine
- les projets de transaction, les manifestes, la signature et les flux de travail des enveloppes de transactions signées
- événements de streaming, filtres et curseurs réalisables
- l'accès à la préparation Kagemusha générique et les aides d'abonnement Torii; les constructeurs de remplissage et de rachat typés ne sont pas exposés.
- l'adresse du compte, les aides à la signature intégrale de l'algorithme, les voyages aller-retour multi-hash, SM2, GOST, ML-DSA, BLS et le traitement confidentiel des clés;
- Connectez URIs, les sessions, les cadres, les aides au chiffrement et l'administrateur du registre
- la gouvernance, la mise à niveau du temps d'exécution, Sumeragi, l'administrateur de nœud, SoraFS, UAID et Kaigi enveloppes des points d'extrémité où le nœud expose ces caractéristiques
Références en amont
python/iroha_python/README.mdpython/iroha_python/DESIGN.mdpython/iroha_python/src/iroha_python
Ces fichiers sont la source de vérité pour la surface Python dans la révision de l'espace de travail coincé.