TON: Empfehlungen und Best Practices

Dieser Artikel ist eine Übersetzung eines Dokuments, das auf der TON-Blockchain-Seite veröffentlicht wurde: smc -richtlinien.txt . Vielleicht hilft dies jemandem, einen Schritt in Richtung Entwicklung für diese Blockchain zu machen. Außerdem habe ich am Ende eine kurze Zusammenfassung gemacht.


Interne Nachrichten


Intelligente Verträge interagieren miteinander, indem sie sogenannte interne Nachrichten senden. Wenn die interne Nachricht ihr angegebenes Ziel erreicht, wird im Namen des Zielkontos eine reguläre Transaktion erstellt und die interne Nachricht gemäß dem angegebenen Code und den konstanten Daten dieses Kontos verarbeitet (Smart Contract). Insbesondere kann eine Verarbeitungstransaktion eine oder mehrere interne Nachrichten erstellen, von denen einige an die Quelladresse der zu verarbeitenden internen Nachricht adressiert sein können. Dies kann verwendet werden, um einfache „Client-Server-Anwendungen“ zu erstellen, wenn eine Anforderung in eine interne Nachricht eingebettet (gekapselt) und an einen anderen intelligenten Vertrag gesendet wird, der die Anforderung verarbeitet und die Antwort erneut als interne Nachricht zurücksendet.


Dieser Ansatz führt dazu, dass zwischen internen Nachrichten an "Anfrage" und "Antwort" (als "Abfrage" oder als "Antwort") unterschieden werden muss oder keine zusätzliche Verarbeitung erforderlich ist (z. B. eine einfache Geldüberweisung). Wenn eine Antwort eintrifft, muss es außerdem eine Möglichkeit geben, zu verstehen, auf welche Anfrage sie sich bezieht.


Um dieses Ziel zu erreichen, wird empfohlen, die folgende interne Nachrichtenvorlage zu verwenden (denken Sie daran, dass die TON-Blockchain dem Nachrichtentext keine Einschränkungen auferlegt, dh es handelt sich lediglich um eine Empfehlung):


0) Der Nachrichtentext kann in die Nachricht selbst eingebettet sein oder in einer separaten Zelle (Zelle *) gespeichert werden, auf die in der Nachricht verwiesen wird, wie im TL-B-Fragment des Diagramms angegeben (auf Englisch ist es einfacher zu verstehen: oder in einer separaten Zelle gespeichert) Zelle, auf die in der Nachricht Bezug genommen wird, wie durch das TL-B-Schemafragment angegeben):


message$_ {X:Type} ... body:(Either X ^X) = Message X; 

( https://core.telegram.org/mtproto - hier können Sie über TL-Schemata lesen)


Der empfangende Smart-Vertrag muss mindestens interne Nachrichten mit eingebettetem Nachrichtentext akzeptieren (selbst wenn sie in der Zelle platziert sind, in der sich die Nachricht befindet - wann immer sie in die Zelle passen, in der sich die Nachricht befindet - ist nicht ganz klar, was dies bedeutet, daher der Originaltext angehängt). Wenn der Vertrag Nachrichtentexte in separaten Zellen akzeptiert (unter Verwendung des "richtigen" Konstruktors (Either X ^X) ), sollte die Verarbeitung der eingehenden Nachricht nicht von der bestimmten Methode zum Einbetten des Nachrichtentexts abhängen. Andererseits ist es absolut legal, den Nachrichtentext überhaupt nicht in einer separaten Zelle zu unterstützen, um Anfragen und Antworten zu vereinfachen.


1) Der Nachrichtentext beginnt normalerweise mit den folgenden Feldern:


  • op - 32-Bit-Ganzzahl ohne Vorzeichen (Big-Endian), die die auszuführende Operation oder die aufzurufende Smart-Contract-Methode angibt.
  • query_id ist eine vorzeichenlose 64-Bit-Ganzzahl (Big-Endian), die in allen internen Frage- und Antwortnachrichten verwendet wird, um die Beziehung der Antwort zur Anforderung zu identifizieren (die Abfrage-ID der Antwort muss der Abfrage-ID der entsprechenden Anforderung entsprechen). Wenn op keine Anforderungs-Antwort-Methode ist (es ruft eine Methode auf, von der keine Antwort erwartet wird), kann query_id weggelassen werden.
  • Der Rest des Nachrichtentexts ist spezifisch für jeden unterstützten Wert des op- Parameters

2) Wenn op Null ist, ist die Nachricht eine einfache Übertragungsnachricht mit einem Kommentar. Der Kommentar ist im Rest der Nachricht enthalten (ohne query_id usw., dh ab dem 5. Byte (Erklärung: Wenn query_id nicht vorhanden ist , nimmt das op- Feld die ersten 4 Bytes ein)). Wenn es nicht mit dem Byte 0xff beginnt, ist der Kommentar ein Text;); Es kann dem Endbenutzer der Brieftasche "wie sie ist" angezeigt werden (nachdem ungültige Zeichen und Steuerzeichen gefiltert und überprüft wurden, ob es sich um eine gültige UTF-8-Zeichenfolge handelt). In diesem Feld können Benutzer beispielsweise den Zweck einer einfachen Übertragung von ihrer Brieftasche auf die Brieftasche eines anderen Benutzers angeben. Wenn ein Kommentar hingegen mit dem Byte 0xff beginnt, ist der Rest der Nachricht ein „binärer Kommentar“, der dem Endbenutzer nicht als Text angezeigt werden sollte (ggf. nur als Hex-Dump). Die vorgeschlagene Verwendung von binären Kommentaren besteht beispielsweise darin, eine Zahlungskennung für die Zahlung im Geschäft zu enthalten und automatisch von der Geschäftssoftware generiert und verarbeitet zu werden.


Die meisten intelligenten Verträge müssen keine nicht trivialen Aktionen ausführen oder eine eingehende Nachricht ablehnen, wenn sie eine „einfache Übertragungsnachricht“ erhalten. Wenn sich herausstellt, dass op Null ist, sollte die Smart Contract-Funktion zum Verarbeiten eingehender interner Nachrichten (normalerweise als recv_internal() ) sofort mit dem Code 0 beendet werden, was auf Erfolg hinweist (z. B. recv_internal() Ausnahme 0, wenn kein benutzerdefinierter Handler im Smart Contract installiert ist Ausnahmen). Dies führt dazu, dass der von der Nachricht überwiesene Betrag ohne weitere Auswirkungen dem Konto des Empfängers gutgeschrieben wird.


3) "Eine einfache Nachrichtenübertragung ohne Kommentare" hat einen leeren Körper (auch ohne das Operationsfeld ). Die obigen Überlegungen gelten für solche Nachrichten. Bitte beachten Sie, dass für solche Nachrichten ein eigener Text in die Nachrichtenzelle eingebettet sein muss.


4) Wir erwarten, dass das Operationsfeld der Anforderungsnachrichten das erste Bit ("höherwertiges Bit", übersetzt als das erste, dies kann falsch sein, aber wie später erläutert wird klar) leer ist, dh der Wert des Feldes sollte im Bereich 1 .. 2^31-1 , und für Antwortnachrichten sollte das erste (höherwertige) Bit gleich 1 sein, 2^31 .. 2^32-1 im Bereich 2^31 .. 2^32-1 . Wenn die Nachricht weder eine Anforderung noch eine Antwort ist (der Body enthält nicht den Parameter query_id ), muss sie den Parameter op im Bereich wie in der Anforderungsnachricht enthalten: 1 .. 2^31 - 1 .


5) Es gibt mehrere "Standard" -Antwortnachrichten, für die op 0xffffffff und 0xffffffffe ist. Im Allgemeinen sind Operationswerte von 0xfffffff0 bis 0xffffffff für solche Standardantworten reserviert.


  • op = 0xffffffff bedeutet "Operation wird nicht unterstützt." Es folgt eine 64-Bit- Abfrage - ID, die aus der ursprünglichen Abfrage extrahiert wurde, und eine 32-Bit- Operation der ursprünglichen Abfrage. Alle außer den einfachsten Smart-Verträgen sollten diesen Fehler zurückgeben, wenn sie eine Anfrage mit einer unbekannten Operation im Bereich von 1 ... 2 ^ 31-1 erhalten.
  • op = 0xfffffffe bedeutet "Operation nicht erlaubt". Es folgt die 64-Bit-Abfrage- ID der ursprünglichen Abfrage und anschließend die 32-Bit- Operation, die aus der ursprünglichen Abfrage extrahiert wurde.

Beachten Sie, dass unbekannte "Antworten" (mit op im Bereich 2 ^ 31 ... 2 ^ 32-1) ignoriert werden sollten (insbesondere sollten Sie keine Antwort mit op gleich 0xffffffff generieren) sowie unerwartete Rückgabe ( bounce) -nachrichten (mit gesetztem "bounce" -Flag).


Zahlung für die Bearbeitung von Anfragen und das Senden von Antworten


Wenn ein Smart-Vertrag eine Anfrage an einen anderen Smart-Vertrag senden möchte, muss er im Allgemeinen für das Senden einer internen Nachricht an den Ziel-Smart-Vertrag (Nachrichtenweiterleitungsgebühren) bezahlen, um diese Nachricht am Zielort zu verarbeiten (Gasgebühr: Gasgebühren) und zum Senden einer Antwort, falls erforderlich (Gebühren für die Weiterleitung von Nachrichten).


In den meisten Fällen fügt der Absender der internen Nachricht eine kleine Menge Gramm hinzu (z. B. 1 Gramm) (genug, um für die Verarbeitung dieser Nachricht zu bezahlen) und setzt das Flag "Bounce" darauf (dh es wird eine bounceable interne Nachricht gesendet). Der Empfänger gibt den nicht verwendeten Teil des empfangenen Werts mit der Antwort zurück (abzüglich der Gebühr für das Senden der Nachricht). Dies wird normalerweise durch Aufrufen von SENDRAWMSG mit mode = 64 erreicht (siehe Anhang A zur TON VM-Dokumentation).


Wenn der Empfänger die empfangene Nachricht nicht verarbeiten kann und die Ausführung mit einem Exit-Code ungleich Null beendet wird (z. B. aufgrund einer Ausnahme bei der Deserialisierung nicht behandelter Zellen), wird die Nachricht automatisch an den Absender zurückgeschickt, und das Flag "Absprung" wird deaktiviert und gesetzt. Flagge "prallte ab". Der Text der zurückgesendeten Nachricht entspricht dem der ursprünglichen Nachricht. Daher ist es wichtig, das "Bounce" -Flag der eingehenden internen Nachricht zu überprüfen, bevor das Operationsfeld im Smart-Vertrag analysiert und die entsprechende Anforderung verarbeitet wird (andernfalls besteht das Risiko, dass die in der Bounce-Nachricht enthaltene Anforderung von ihrem ursprünglichen Absender als neue separate Anforderung verarbeitet wird). Wenn das Flag "Bounce" gesetzt ist, kann der Spezialcode verstehen, welche Anforderung fehlgeschlagen ist (z. B. durch Deserialisieren von op und query_id aus einer Bounce- Nachricht) und entsprechende Maßnahmen ergreifen. Ein einfacherer Smart-Vertrag kann einfach alle zurückgegebenen Nachrichten ignorieren (mit einem Exit-Code von Null beenden, wenn das Flag "Bounce" gesetzt ist).


Andererseits kann der Empfänger die eingehende Anforderung erfolgreich analysieren und feststellen, dass die angeforderte Operationsmethode nicht unterstützt wird oder dass eine andere Fehlerbedingung erfüllt wurde. Dann sollte eine Antwort mit op gleich 0xffffffff oder einem anderen geeigneten Wert unter Verwendung von SENDRAWMSG mit mode = 64 zurückgesendet werden, wie oben erwähnt.


In einigen Situationen möchte der Absender gleichzeitig einen bestimmten Geldbetrag überweisen? an den Absender? (hier anscheinend ein Fehler und war für den "Empfänger" bestimmt) und erhalten entweder eine Bestätigung oder eine Fehlermeldung. Beispielsweise erhält ein intelligenter Vertrag für Validatorwahlen eine Aufforderung zur Teilnahme an einer Wahl zusammen mit einem Angebot als Mehrwert. In solchen Fällen ist es sinnvoll, beispielsweise ein zusätzliches Gramm an den geschätzten Wert [Kosten] anzuhängen (hier wird der Wortwert überall verwendet, im Sinne einer Zahlung für eine Aktion, daher habe ich das Wort "Kosten" verwendet). Wenn ein Fehler auftritt (z. B. kann das Gebot aus irgendeinem Grund nicht angenommen werden), sollte der gesamte erhaltene Betrag (abzüglich der Bearbeitungsgebühr) zusammen mit der Fehlermeldung an den Absender zurückgesandt werden (z. B. unter Verwendung von SENDRAWMSG mit mode = 64, wie beschrieben) oben). Bei Erfolg wird eine Bestätigungsnachricht erstellt und genau ein Gramm zurückgesendet (die Nachrichtenübertragungsgebühr wird von diesem Wert abgezogen; dies ist Modus = 1 von SENDRAWMSG).


Verwenden von nicht bounceable Nachrichten


Fast alle internen Nachrichten, die zwischen intelligenten Verträgen gesendet werden, sollten zurückgegeben werden (Sie können sie als "Bouncing" übersetzen, aber um nicht verwirrt zu werden, ist es einfacher, diese Terminologie zu verwenden), dh das Bit "Bounce" muss nicht leer sein. Wenn der Smart-Zielvertrag nicht vorhanden ist oder bei der Verarbeitung dieser Nachricht eine unbehandelte Ausnahme entsteht, wird die Nachricht mit dem Rest der anfänglichen Kosten (Wert) (abzüglich aller Gebühren für die Übertragung von Nachrichten und Gas) zurückgesendet. Die zurückgegebene Nachricht hat denselben Text, jedoch mit deaktiviertem "Bounce" -Flag und gesetztem "Bounce" -Flag. Daher sollten alle Smart-Verträge das "Bounce" -Flag aller eingehenden Nachrichten überprüfen und diese entweder stillschweigend empfangen (sofort mit einem Null-Exit-Code beenden) oder eine spezielle Verarbeitung durchführen, um festzustellen, welche ausgehende Anforderung fehlgeschlagen ist. Die im Hauptteil der zurückgegebenen Nachricht enthaltene Anforderung sollte niemals ausgeführt werden.


In einigen Fällen müssen nicht bounceable interne Nachrichten verwendet werden. Beispielsweise kann ein neues Konto nicht erstellt werden, ohne dass mindestens eine unwiderrufliche interne Nachricht an das Konto gesendet wurde. Wenn diese Nachricht kein StateInit mit dem Code und den Daten des neuen Smart-Vertrags enthält, ist es nicht sinnvoll, einen nicht leeren Text in einer nicht zurückkehrenden internen Nachricht zu haben.


Es ist eine gute Idee, dem Endbenutzer (z. B. der Brieftasche) nicht zu erlauben, unwiderrufliche Nachrichten zu senden, die eine große Menge enthalten (z. B. mehr als fünf Gramm), oder sie zumindest zu warnen, wenn sie dies versuchen. Es ist besser, zuerst einen kleinen Betrag zu senden, dann einen neuen Smart-Vertrag zu erstellen und dann einen größeren Betrag zu senden.


Externe Nachrichten


Externe Nachrichten werden extern an intelligente Verträge in der TON-Blockchain gesendet, um sie zur Ausführung bestimmter Aktionen zu zwingen. Beispielsweise erwartet der Smart-Vertrag der Brieftasche den Empfang externer Nachrichten mit Bestellungen (z. B. interne Nachrichten, die vom Smart-Vertrag der Brieftasche gesendet werden), die vom Brieftaschenbesitzer unterzeichnet wurden. Wenn eine solche externe Nachricht vom Smart Contract der Brieftasche empfangen wird, überprüft sie zuerst die Signatur, empfängt dann die Nachricht (durch Starten des TVM ACCEPT-Grundelements) und führt dann alle erforderlichen Aktionen aus.


Bitte beachten Sie, dass alle externen Nachrichten vor Wiederholungsangriffen geschützt werden müssen. Validatoren entfernen normalerweise eine externe Nachricht aus dem Pool der vorgeschlagenen externen Nachrichten (vom Netzwerk empfangen). In einigen Situationen kann ein anderer Prüfer dieselbe externe Nachricht jedoch zweimal verarbeiten (wodurch eine zweite Transaktion für dieselbe externe Nachricht erstellt wird, was zu einer Verdoppelung der ursprünglichen Aktion führt). Schlimmer noch, ein Angreifer kann eine externe Nachricht aus einem Block mit einer Verarbeitungstransaktion extrahieren und später erneut senden. Dies kann beispielsweise dazu führen, dass ein Smart-Wallet-Vertrag die Zahlung wiederholt.


Der einfachste Weg, intelligente Verträge vor Sniffing-Angriffen zu schützen, die mit externen Nachrichten verbunden sind, besteht darin, den 32-Bit- Cur-Seqno-Zähler in den konstanten Daten des intelligenten Vertrags zu speichern und auf den Wert für die Anforderung im (signierten Teil) aller eingehenden externen Nachrichten zu warten. Dann wird die externe Nachricht nur akzeptiert (AKZEPTIERT - ein Hinweis auf das primitive AKZEPTIEREN), wenn die Signatur gültig ist und req-seqno gleich cur-seqno ist . Nach erfolgreicher Verarbeitung erhöht sich der Wert von cur-seqno in persistenten Daten um eins, sodass dieselbe externe Nachricht nie wieder empfangen wird.


Sie können das Ablaufdatum auch in eine externe Nachricht aufnehmen und die Nachricht nur akzeptieren, wenn die aktuelle Unix-Zeit unter dem Wert dieses Felds liegt. Dieser Ansatz kann in Kombination mit seqno verwendet werden ; Alternativ kann der empfangende Smart-Vertrag einen Satz (Hashes) aller zuletzt (nicht abgelaufenen) empfangenen externen Nachrichten in seinen permanenten Daten speichern und eine neue externe Nachricht ablehnen, wenn es sich um ein Duplikat einer der gespeicherten Nachrichten handelt. Sie sollten auch das Sammeln und Entfernen abgelaufener Nachrichten in diesem Satz implementieren, um ein unbegrenztes Wachstum persistenter Daten zu vermeiden.


In der Regel beginnt eine externe Nachricht mit einer 256-Bit-Signatur (falls erforderlich), einer 32-Bit -Anforderungsnummer (falls erforderlich), einem 32-Bit -Ablaufdatum (falls erforderlich) und möglicherweise einer 32-Bit -Operation und anderen erforderlichen Parametern in je nach op . Die externe Nachrichtenvorlage muss nicht so standardisiert sein wie die interne Nachrichtenvorlage, da externe Nachrichten nicht für die Interaktion zwischen verschiedenen intelligenten Verträgen verwendet werden (von verschiedenen Entwicklern geschrieben und von verschiedenen Eigentümern verwaltet).


Methoden abrufen


Von einigen intelligenten Verträgen wird erwartet, dass sie bestimmte genau definierte Get-Methoden implementieren. Beispielsweise wird erwartet, dass jeder DNS-Resolver-Smart-Vertrag für TON DNS die Methode dnsresolve get implementiert. Benutzerdefinierte Smart-Verträge können ihre spezifischen Get-Methoden definieren. Unsere einzige allgemeine Empfehlung im Moment ist die Implementierung der get-Methode "seqno" (ohne Parameter), die die aktuelle seqno des Smart-Vertrags zurückgibt , die Sequenznummern verwendet, um Wiedergabeangriffe zu verhindern, die mit eingehenden externen Methoden verbunden sind, wann immer eine solche Methode vorhanden ist Bedeutung.


Wörterbuch:


  • Zelle - Eine TVM-Zelle besteht aus höchstens 1023 Datenbits und höchstens vier Verweisen auf andere Zellen. Alle persistenten Daten (einschließlich TVM-Code) in der TON-Blockchain werden als Sammlung von TVM-Zellen dargestellt (vgl. [1, 2.5.14]). - Eine TVM-Zelle besteht aus nicht mehr als 1023 Datenbits und nicht mehr als vier Verbindungen zu anderen Zellen. Alle persistenten Daten (einschließlich des TVM-Codes) in der TON-Blockchain werden als Satz von TVM-Zellen dargestellt (vgl. [1, 2.5.14]). - Auszug aus der Beschreibung der TON Virtual Machine ( https://test.ton.org/tvm.pdf )

Welche Schlussfolgerungen können aus dem, was ich lese, gezogen werden?


  1. Sie können externe Nachrichten an Verträge senden, um eine Aktion auszulösen.
  2. Angriffe - Es gibt zum Beispiel Wiederholungsangriffe
  3. Es lohnt sich, die seqno- Methode zum Schutz vor Wiederholungsangriffen zu verwenden.
  4. DNS-Resolver haben die DNS-Auflösungsmethode
  5. Sie können Hashes externer Nachrichten speichern, um sich vor Angriffen zu schützen. Sie müssen sie jedoch rechtzeitig löschen. Verwenden Sie dazu das Feld expired_at für externe Nachrichten
  6. Nicht zurückgegebene Nachrichten werden nur zum Erstellen von Verträgen benötigt, andernfalls werden alle internen Nachrichten zurückgegeben
  7. Anforderungs- / Antwortnachrichten sollten die folgenden Felder enthalten: op, query_id - optional und einige weitere, abhängig vom Wert von op
  8. Sie können Textkommentare im UTF-8-Format für Personen und „binäre Kommentare“ zum automatischen Lesen und Verarbeiten durch Software von Drittanbietern anhängen.
  9. Es lohnt sich, mit Ausnahmen umzugehen und dies mit Bedacht zu tun
  10. "Einfache Nachricht ohne Kommentare" - muss einen leeren Körper haben
  11. Das höherwertige Bit von Anforderungs-Antwort-Nachrichten nimmt für Anforderungsnachrichten den Wert 0 und für Antwortnachrichten den Wert 1 an
  12. Es gibt Standard-Operationswerte für Antwortnachrichten, um Fehler zu identifizieren
  13. Wenn eine Antwortnachricht mit einer unbekannten Operation empfangen wird, sollte sie ignoriert werden, dh die vollständige Ausführung mit Code 0
  14. Sie müssen für das Senden von Nachrichten, für Benzin und für das Senden einer Antwort bezahlen. Zur gleichen Zeit, wenn er mehr als nötig gesendet hat, wird der Überschuss in der Antwort zurückgegeben.
  15. Beim Empfang von Nachrichten lohnt es sich immer, zuerst das zurückgeworfene Flag zu überprüfen .

Vielen Dank für Ihre Aufmerksamkeit, ich freue mich über konstruktives Feedback!

Source: https://habr.com/ru/post/de470772/


All Articles