Provider integrieren
Betreibe einen URnetwork-Provider in deiner App. Jede Installation teilt die Verbindung als Client deines Netzwerks und verdient in ein festes Bittensor-Wallet.
Ein Provider teilt die Internetverbindung des Geräts: Der Datenverkehr anderer URnetwork-Nutzer tritt über sie aus. Während er läuft, zeigt deine App vier Dinge: ob er die Verbindung teilt, die bedienten Clients, die bereitgestellten Daten und das Auszahlungs-Wallet, nur lesend. Die Summen der Einnahmen kommen von deinem Backend. Lauffähige Beispiele gibt es für zwölf Sprachen und drei eigene Plattformen: Electron, Tauri und Android. Alle setzen den Provider-Vertrag um, der die genauen Regeln festlegt; diese Seite erklärt, wie die Teile zusammenpassen.
Wer was hält
Eine Provider-Integration teilt die Arbeit auf drei Beteiligte auf, und kein Schlüssel wird mit einer App ausgeliefert.
- Dein Backend hält das Root-JWT deines Netzwerks. Es richtet jede Installation als Provider-Installation (provider install) ein, liefert jeder Installation ihr eingeschränktes Client-JWT und fordert die Zustimmungsnachrichten für das Wallet an.
- Der Inhaber des Coldkeys signiert jede Wallet-Zustimmung offline, mit seinem eigenen Wallet-Werkzeug wie btcli, der polkadot.js-Erweiterung oder subkey. Das Geheimnis des Coldkeys erreicht nie dein Backend oder die App.
- Die App betreibt das lokale Device des SDK mit ihrem Client-JWT, teilt die Verbindung öffentlich und zeigt den Status. Sie hält nie das Root-JWT und signiert oder übermittelt nie eine Zustimmung.
| Wert | Ort | Zweck |
|---|---|---|
URNETWORK_ | Secret-Speicher deines Backends | Richtet Provider-Installationen ein und ordnet sie dem Auszahlungs-Wallet zu |
| Adresse des Auszahlungs-Coldkeys (ss58) | Konfiguration deines Backends | Das feste Auszahlungs-Wallet: öffentlich, aber nur vom Backend gesetzt |
| Geheimnis des Coldkeys | Beim Inhaber, offline | Signiert die Wallet-Zustimmungen |
client.jwt | Privater Zustand der Installation | Der eingeschränkte Zugangsnachweis der Installation, den das SDK erneuert |
Ausgezahlt wird an den Coldkey, als Auszahlung des Bittensor-Subnetzes. Die App liest das Auszahlungs-Wallet nur, um es anzuzeigen, und hat keine eigenen Auszahlungseinstellungen.
Zuerst die Zustimmung
Beim Teilen tritt der Datenverkehr anderer über die Verbindung des Nutzers aus, also muss deine App vorher um Zustimmung bitten und jederzeit ein Beenden erlauben. Jedes Beispiel zeigt diesen Hinweis wörtlich, und der Vertrag legt seinen SHA-256 fest:
Consent disclaimer: an app that integrates a URnetwork provider must collect the user's consent before it provides.
Providing shares the user's internet connection: other URnetwork users' traffic exits through the user's device and IP address.
This example starts providing without asking, because the consent screen belongs to your app.Konsolenbeispiele geben ihn beim Start aus. GUI- und Android-Apps zeigen ihn neben dem Bedienelement, das das Teilen startet. Die Beispiele haben keinen eigenen Zustimmungsbildschirm, denn dieser Bildschirm gehört zu deiner App, und sie begrenzen die Bandbreite nicht.
Provider-Installationen einrichten
Jede Installation ist eine Provider-Installation (provider install): ein Top-Level-Client deines Netzwerks, der nur seine Verbindung teilt. Dein Backend legt sie mit POST /network/ und dem Root-JWT an und sendet eine Beschreibung, eine Gerätespezifikation und das Flag provide_, aber weder client_id noch source_:
{
"description": "provider user:alice:laptop-1",
"device_spec": "urnetwork-examples/ go-provider",
"provide_intent": true
}Die Antwort enthält die neue client_id und ihr eingeschränktes by_, das dein Backend der Installation als ihr client.jwt übergibt. Der Server speichert das Flag für die gesamte Lebensdauer des Clients:
- Die Provider-Installation erscheint nie in der Peer-Liste deines Netzwerks (
GET /network/und der Strom der Peer-Änderungen), und sie erhält die Liste nie.peers - Sie zählt weder zur Obergrenze von 100 Top-Level-Clients noch zu den 100 kürzlich aktiven Top-Level-Clients, die die Peer-Liste eingeschaltet halten.
- Ihre Erstellung ist von dieser Obergrenze und von der Prüfung gleichzeitiger Clients deines Plans ausgenommen.
Das Flag bringt das Gerät nicht zum Teilen. Das SDK erklärt die Bereitstellungsabsicht (provide intent) auf seinen eigenen Verbindungen, sobald das Gerät öffentlich teilt, und die Regeln zum Client-Limit unten gelten für Verbindungen, die sie erklären. Apps stellen dafür nichts ein. Eine Provider-Installation, die sich verbindet, ohne öffentlich zu teilen, zählt als normaler Client.
Um ein Token neu auszustellen, sende die gespeicherte client_id mit der Beschreibung und der Gerätespezifikation. Der Server ignoriert provide_ bei einer Neuausstellung, also sende das Flag nur beim Anlegen. Ein Client, der sich 30 Tage nicht verbunden hat, wird deaktiviert: Seine Neuausstellung antwortet mit Client does not exist., und dein Backend richtet dann eine neue Provider-Installation ein.
Zwei Arten von Clients sind keine Provider-Installationen. Das eigene Gerät einer Person, das ebenfalls teilt, erklärt die Absicht selbst und bleibt ein Peer des Netzwerks, also setze das Flag dafür nicht. Ein Child-Client, eingerichtet mit einer source_, wird anderen Nutzern nie als Provider angeboten und verdient nichts.
Das Go-Backend-Werkzeug erledigt all das mit wallet provision <installation-key> <client-jwt-file>, und der Vertrag zeigt dieselben Anfragen mit curl.
Provider und das Client-Limit
Provider sind vom Limit gleichzeitiger Clients deines Plans ausgenommen, solange sie sich qualifizieren und sobald sie sich qualifiziert haben. Ein Provider qualifiziert sich mit drei Dingen:
- dem öffentlichen Teilen der Verbindung, mit registrierten Provide-Schlüsseln (provide keys);
- einer bestandenen Egress-Health-Probe;
- sobald er 12 Stunden Verlauf hat, einer 12-Stunden-Zuverlässigkeit von mindestens 0.7.
Ein neuer Provider hat 1 Stunde Schonfrist, um sich zu qualifizieren, und diese Frist endet nie, bevor mindestens eine Egress-Probe versucht wurde. Ein qualifizierter Provider wird alle 4 Stunden erneut geprüft. Jeder Client hat einen Qualifizierungsversuch pro 8 Stunden.
Ein Provider, der sich nicht qualifiziert, belegt einen normalen Client-Platz, wenn einer frei ist. Ist keiner frei, kann die Plattform ihn trennen. Das SDK hält dann jede Plattformverbindung dieses Clients für 15 Minuten plus bis zu 5 Minuten Zufallsversatz (Jitter) zurück und verbindet sich danach von selbst neu. In der Zwischenzeit zeigt die App client limit, retry at HH:MM UTC: die Zeit des nächsten Versuchs in UTC, auf eine ganze Minute aufgerundet. Darüber hinaus erfährt der Client von der Plattform nichts über die Qualifizierung.
Das Auszahlungs-Wallet zuordnen
Das Auszahlungs-Wallet ist dein fester Bittensor-Coldkey, als ss58-Adresse. Der Server zahlt einen Provider-Client nur über eine Zustimmung zur Wallet-Zuordnung aus, die der Inhaber des Coldkeys signiert hat. Es gibt zwei Arten:
| Zustimmung | Eine Signatur deckt ab | Verwende sie für |
|---|---|---|
| Netzwerk-Zustimmung | Alle Provider-Clients deines Netzwerks, heutige und künftige | Apps, die einen Provider einbetten: Der Inhaber signiert einmal pro Netzwerk |
| Zustimmung pro Provider | Einen Provider-Client | Einen Client, der in einen anderen Coldkey verdient, etwa den eines unabhängigen Unterzeichners |
Für jeden Provider-Client und jede Epoche gewinnt eine gültige Zustimmung pro Provider; sonst zahlt die Netzwerk-Zustimmung. Eine Zustimmung gilt für die Epochen von ihrer from_epoch bis zu ihrer through_, höchstens 65.536, und nur in die Zukunft. Um den Coldkey zu wechseln, signiere eine neue Zustimmung derselben Art; frühere Epochen behalten den früheren Coldkey. Einen Widerruf ohne Signatur gibt es nicht.
Jede Zustimmung braucht vier Schritte, und nur die Signatur kommt von außerhalb deines Backends:
- Lies die aktuelle Epoche
EausGET /sn/epoch. - Fordere die Nachricht mit dem Root-JWT an:
POST /sn/mit dem Coldkey und den Epochenwallet/ network-consent E+1bisE+65536, oderPOST /sn/zusätzlich mit einerwallet/ consent client_id. - Der Inhaber des Coldkeys signiert die exakten Bytes der Nachricht innerhalb von fünf Minuten.
- Sende den Coldkey, die Nachricht und die Signatur an
POST /sn/wallet, bei einer Zustimmung pro Provider mit derselbenclient_id. Prüfe die Zuordnung mitGET /sn/wallet.
Die Signatur ist sr25519 im Signaturkontext substrate über die UTF-8-Bytes der Nachricht, roh oder als <Bytes>…</ umhüllt. Die umhüllte Form ist das, was signRaw vom Typ bytes in einer Polkadot-Erweiterung signiert. Die Signatur ist 64 Bytes lang und wird als Hex gesendet. Eine Nachricht für eine Netzwerk-Zustimmung beginnt mit Approve URnetwork network wallet mapping, eine pro Provider mit Approve URnetwork provider wallet mapping.
Das Go-Wallet-Werkzeug beherrscht beide Arten: network-challenge und network-accept für die Netzwerk-Zustimmung, challenge und accept für einen Client und show zum Auflisten. Ein Server ohne Netzwerk-Zustimmungen beantwortet POST /sn/ mit 404; Zustimmungen pro Provider funktionieren dort weiterhin. Ein dritter, nachrangiger Modus, die Hotkey-Delegation, kommt mit einer späteren Änderung an Server und SDK. Apps kennzeichnen ihn nur mit (hotkey).
In der App
Jede Installation hält ihren Zustand in einem einzigen privaten Verzeichnis: client.jwt, instance-id, identity.json und die begrenzten logs/ des SDK. Konsolenbeispiele lesen den absoluten Pfad des Verzeichnisses aus URNETWORK_; GUI- und Android-Apps nutzen ihre privaten Anwendungsdaten. Unter POSIX hat das Verzeichnis die Rechte 0700 und seine Dateien 0600, und jede Datei wird atomar ersetzt. identity.json erhält die Schlüssel des Providers über Neustarts hinweg.
Der Lebenszyklus der App ist kurz:
- Zeige den Zustimmungshinweis und lade den Zustand der Installation.
- Erzeuge das Device mit dem Extender-fähigen Konstruktor des SDK und dem Schlüsselmaterial der Installation. Beim ersten Start gibt es keines: Das Device erzeugt eine neue Identität, die die App als
identity.jsonspeichert. - Speichere jedes erneuerte Token als
client.jwt, und halte bei einer Abmeldung an. - Stelle das Teilen der Verbindung auf öffentlich. Das SDK erklärt dann selbst die Bereitstellungsabsicht.
- Lies den Status jede Sekunde und das Auszahlungs-Wallet beim Start und alle 10 Minuten.
Jedes Beispiel zeigt dieselben vier Felder mit denselben Regeln:
| Feld | Zeigt |
|---|---|
| Status | stopped, client limit, paused, providing oder starting, in dieser Reihenfolge geprüft |
| Bediente Clients | Verschiedene Clients, die seit dem Start der App einen Vertrag mit diesem Provider geöffnet haben |
| Bereitgestellte Daten | Für Clients weitergeleitete Bytes in beide Richtungen, in binären Einheiten |
| Auszahlungs-Wallet | Der Coldkey mit seinem Geltungsbereich: (this provider), (network), (hotkey) oder (another provider) |
Konsolenbeispiele geben eine Statuszeile aus, sobald sich der Status, die Zahl der bedienten Clients oder das Auszahlungs-Wallet ändert, und sonst einmal pro Minute:
status: providing | clients served: 3 | data provided: 12.4 MiB | payout wallet: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY (network)providing setzt voraus, dass das Teilen aktiviert und mit der Plattform verbunden ist. Das Wallet zeigt checking vor dem ersten Lesen, unavailable, wenn dieses Lesen fehlschlägt, und not set, wenn kein Wallet zugeordnet ist. Die Adresse oben ist das öffentliche Entwicklungskonto von Substrate und dient nur als Beispieldatum. Die Referenzvektoren des Vertrags legen jeden Text exakt fest.
Die Extender-Rolle
Während es teilt, betreibt das Device auch die Extender-Rolle des Providers, die standardmäßig an ist. Sie lauscht auf TCP 443 und auf UDP 443 und 4053, damit Clients, die die Plattform nicht direkt erreichen, sich über diesen Provider verbinden können. Nur der Kommandozeilen-Provider des Subnetzes (provider provide) lauscht zusätzlich auf UDP 53, und Clients versuchen es sowohl auf UDP 53 als auch auf 4053. Windows und macOS fragen den Nutzer beim ersten Mal womöglich, ob eingehende Verbindungen erlaubt sind. Unter Linux teilt ein Prozess ohne das Recht, diese Ports zu belegen, seine Verbindung ohne die Rolle weiter. Die Android- und iOS-Builds des SDK enthalten sie nicht.
Eine einzige Einstellung schaltet den Standard aus: DefaultProvideExtender auf false.
| Binding | Standard aus |
|---|---|
| Go | DeviceLocalSettings., oder false als letztes Argument des Konstruktors |
| gomobile | Sdk. (Java, Kotlin) oder SdkNewDeviceLocalWithProvideExtender (Swift), mit dem Standard auf false |
| C-ABI | urnet_ mit default_ auf false |
| C++ | urnet:: mit dem Standard auf false |
Die eigene Wahl des Nutzers, gesetzt mit SetProvideExtender, hat weiterhin Vorrang vor dem Standard. ProvideExtenderEnabled auf false ist der Hauptschalter: Die Rolle läuft dann nie. Beide Einstellungen und der Konstruktor, der sie annimmt, sind ab dem ersten Release nach sdk c638dfa8 im SDK.
Im Hintergrund ausführen
Die Konsolenbeispiele teilen sich drei Vorlagen für den Hintergrundbetrieb: einen systemd-Benutzerdienst unter Linux, einen launchd-Agenten unter macOS und eine geplante Aufgabe unter Windows. Jede Vorlage nennt das Zustandsverzeichnis der Installation, also enthält die Dienstdefinition einen Pfad, nie einen Zugangsnachweis. Alle Beispiele verwenden dieselben Exit-Codes:
| Exit-Code | Bedeutung |
|---|---|
| 0 | Ein angeforderter Stopp, etwa mit Ctrl-C oder SIGTERM |
| 78 | Ein Konfigurations- oder Zugangsproblem, das ein Neustart nicht behebt |
| 1 | Jeder andere Fehler |
Supervisoren starten einen Provider nach einem Fehler neu, aber nicht nach 0 oder 78. launchd kennt keine Regel pro Exit-Code: Nach 78 versucht es weiter einmal pro Minute, bis der Zustand behoben ist.
Die GUI- und Android-Apps teilen von sich aus weiter. Electron und Tauri bleiben im Tray, wenn das Fenster geschlossen wird, und bieten einen Start bei der Anmeldung an. Android teilt aus einem Vordergrunddienst, den der Nutzer startet, und pausiert das Teilen in getakteten Netzen.
Beispiele
Das README jedes Beispiels enthält Build- und Ausführungsschritte für jedes unterstützte Betriebssystem und einen Selbsttest ohne Zugangsdaten. Die Konsolenbeispiele laufen unter Windows, macOS und Linux. Der Go-Provider ist die Referenzimplementierung.
| Plattform | Beispiel | Basis |
|---|---|---|
| C | C-Provider | Die C-ABI |
| C++ | C++-Provider | Die C-ABI |
| C# | C#-Provider | Die C-ABI, über .NET |
| Go | Go-Provider | Das Go SDK |
| Java | Java-Provider | Die C-ABI, über JNA |
| JavaScript | JavaScript-Provider | Node und der native Companion |
| Kotlin | Kotlin-Provider | Die C-ABI, über JNA |
| Python | Python-Provider | Die C-ABI, über ctypes |
| Ruby | Ruby-Provider | Die C-ABI, über FFI |
| Rust | Rust-Provider | Die C-ABI, über das Rust-Crate |
| Swift | Swift-Provider | Die C-ABI, über ein SwiftPM-C-Modul |
| TypeScript | TypeScript-Provider | Node und der native Companion |
| Electron | Electron-Provider | Eine Desktop-App auf dem nativen Companion |
| Tauri | Tauri-Provider | Eine Desktop-App mit dem Rust-Crate im selben Prozess |
| Android | Android-Provider | Das gomobile-AAR, in einem Vordergrunddienst |
Verfügbarkeit der Releases: Die Provider-Beispiele brauchen ein SDK-Release mit den Provider-APIs: dem Extender-fähigen Konstruktor, dem Status des Client-Limits und dem Geltungsbereich der Wallet-Zustimmung. Sie sind ab dem ersten Release nach sdk c638dfa8 im SDK. Das SDK installieren erklärt die Pakete, und der Beispiel-Browser enthält die übrigen Beispiele jeder Sprache.