Wie man einen Smart Contract in TON schreibt und veröffentlicht
Worüber handelt dieser Artikel?
In diesem Artikel werde ich erzählen, wie ich am ersten (von zwei) Wettbewerben von Telegram im Bereich Blockchain teilgenommen habe, keinen Preis gewonnen habe und beschloss, meine Erfahrungen in einem Artikel festzuhalten, damit sie nicht in Vergessenheit geraten und vielleicht jemandem helfen.
Da ich keinen abstrakten Code schreiben wollte, sondern etwas Funktionierendes machen wollte, habe ich für den Artikel einen Smart Contract für eine Sofortlotterie sowie eine Website erstellt, die die Daten des Smart Contracts direkt aus TON anzeigt, ohne Zwischenablagen zu verwenden.
Der Artikel wird nützlich sein für diejenigen, die ihren ersten Smart Contract in TON erstellen möchten, aber nicht wissen, wo sie anfangen sollen.
Am Beispiel der Lotterie werde ich von der Einrichtung der Umgebung bis zur Veröffentlichung des Smart Contracts, der Interaktion mit ihm und dem Schreiben einer Website zur Abfrage und Veröffentlichung von Daten gehen.
Über die Teilnahme am Wettbewerb
Im Oktober letzten Jahres kündigte Telegram einen Blockchain-Wettbewerb mit neuen Sprachen an. Fift und FunC. Es war erforderlich, einen der fünf vorgeschlagenen Smart Contracts zu schreiben. Ich dachte, es wäre gut, mich mit etwas Ungewöhnlichem zu beschäftigen, die Sprache zu lernen und etwas zu tun, auch wenn ich in Zukunft nichts mehr schreiben sollte. Außerdem ist das Thema ständig aktuell.
Es sei gesagt, dass ich keine Erfahrung in der Entwicklung von Smart Contracts hatte.
Ich plante, bis zum Ende teilzunehmen, solange es möglich war, und danach einen Überblicksartikel zu schreiben, aber ich bin sofort bei der ersten Herausforderung gescheitert. Ich mit Multisig für FunC geschrieben und sie hat im Großen und Ganzen funktioniert. Als Grundlage nahm ich .
Zu dem Zeitpunkt dachte ich, das wäre definitiv genug, um zumindest einen Preis zu gewinnen. Letztlich wurden etwa 40 von 60 Teilnehmern zu Preisträgern und ich war nicht unter ihnen. Insgesamt ist das nicht schlimm, aber eine Sache hat mich gestresst. Zum Zeitpunkt der Bekanntgabe der Ergebnisse wurde kein Review-Test zu meinem Contract durchgeführt. Ich fragte die Teilnehmer im Chat, ob es noch jemanden gab, der keinen hatte, aber es gab niemanden.
Offensichtlich bemerkten die Richter nach meinen Nachrichten zwei Tage später, dass sie einen Kommentar veröffentlichten, und ich verstand immer noch nicht, ob sie meinen Smart Contract während der Prüfung versehentlich übersprungen hatten oder einfach dachten, dass er so schlecht sei, dass er keinen Kommentar benötigte. Ich stellte eine Frage auf der Seite, erhielt jedoch keine Antwort. Wer beurteilt hat, ist kein Geheimnis; ich hielt persönliche Nachrichten für überflüssig.
Es wurde viel Zeit mit dem Verständnis verbracht, daher wurde beschlossen, einen Artikel zu schreiben. Da es noch nicht viel Informationen gibt, wird der Artikel allen Interessierten helfen, Zeit zu sparen.
Das Konzept der Arbeit mit Smart Contracts in TON
Bevor wir etwas schreiben, müssen wir klären, aus welcher Perspektive wir dieses Thema überhaupt angehen. Daher werde ich jetzt erläutern, aus welchen Teilen das System besteht. Genauer gesagt, welche Teile man kennen muss, um zumindest einen funktionierenden Vertrag zu schreiben.
Wir konzentrieren uns auf das Schreiben von Smart Contracts und die Arbeit mit TON Virtual Machine (TVM), Fift und FunC, daher ähnelt der Artikel eher einer Beschreibung der Entwicklung eines gewöhnlichen Programms. Wir werden hier nicht darauf eingehen, wie die Plattform selbst funktioniert.
Es gibt eine gute offizielle Dokumentation über die Funktionsweise von TVM und der Sprache Fift . Derzeit gibt es keine Dokumentation dazu, daher muss man, um etwas zu schreiben, Beispiele für Smart Contracts aus dem offiziellen Repository studieren und auch die Implementierung der Sprache dort. Außerdem kann man Beispiele für Smart Contracts aus den letzten zwei Wettbewerben einsehen. Die Links dazu finden Sie am Ende des Artikels.
Angenommen, wir haben bereits einen Smart Contract in FunCgeschrieben, danach kompilieren wir den Code in Fift-Assembler.
Der kompilierte Smart Contract muss veröffentlicht werden. Dazu muss eine Funktion in FunCgeschrieben werden, die als Eingabe den Code des Smart Contracts und einige andere Parameter entgegennimmt, und das Ausgabeergebnis ist eine Datei mit der Endung
.boc Fift(was "bag of cells" bedeutet), und je nachdem, wie wir es schreiben, der private Schlüssel und die Adresse, die auf der Grundlage des Codes des Smart Contracts generiert werden. An die Adresse des Smart Contracts, die noch nicht veröffentlicht ist, können bereits Grams gesendet werden. Um den Smart Contract in TON zu veröffentlichen, muss die erhaltene Datei mit Hilfe eines Light Clients an die Blockchain gesendet werden (weiter unten mehr dazu). Aber bevor wir veröffentlichen, müssen wir Grams an die generierte Adresse überweisen, sonst wird der Smart Contract nicht veröffentlicht. Nach der Veröffentlichung kann mit dem Smart Contract interagiert werden, indem ihm von außen Nachrichten gesendet werden (zum Beispiel mit Hilfe eines Light Clients) oder von innen (zum Beispiel sendet ein Smart Contract einem anderen eine Nachricht innerhalb von TON).
Um den Smart Contract in TON zu veröffentlichen, muss die erhaltene Um den Smart Contract in TON zu veröffentlichen, muss die erhaltene Datei mithilfe eines Light Clients an die Blockchain gesendet werden (darüber später mehr). Aber bevor man veröffentlicht, muss man Grams an die generierte Adresse überweisen, sonst wird der Smart Contract nicht veröffentlicht. Nach der Veröffentlichung kann man mit dem Smart Contract interagieren, indem man ihm Nachrichten von außen (zum Beispiel über einen Light Client) oder von innen (zum Beispiel sendet ein Smart Contract einem anderen eine Nachricht innerhalb von TON) sendet.
Nachdem wir verstanden haben, wie der Code veröffentlicht wird, wird der Rest einfacher. Wir wissen ungefähr, was wir schreiben möchten und wie unser Programm funktionieren wird. Während des Schreibens suchen wir, wie dies bereits in bestehenden Smart Contracts umgesetzt wurde, oder schauen in den Implementierungscode. Fift und FunC im offiziellen Repository oder betrachten die offizielle Dokumentation.
Sehr oft habe ich nach Schlüsselwörtern im Telegram-Chat gesucht, wo alle Teilnehmer des Wettbewerbs und auch Mitarbeiter von Telegram versammelt waren. Während des Wettbewerbs haben sich alle genau dort getroffen und über Fift und FunC diskutiert. Der Link befindet sich am Ende des Artikels.
Es ist Zeit, von der Theorie zur Praxis überzugehen.
Vorbereitung der Umgebung für die Arbeit mit TON
Alles, was in diesem Artikel beschrieben wird, habe ich auf MacOS durchgeführt und in einer sauberen Ubuntu 18.04 LTS unter Docker überprüft.
Das erste, was zu tun ist, ist, herunterzuladen und zu installieren lite-client mit dem man Anfragen an TON senden kann.
Die Anleitung auf der offiziellen Website beschreibt den Installationsprozess recht detailliert und überspringt einige Details. Hier folgen wir der Anleitung und installieren dabei fehlende Abhängigkeiten. Ich habe nicht selbst jedes Projekt kompiliert und habe aus dem offiziellen Ubuntu-Repository installiert (auf MacOS habe ich brew).
apt -y install git
apt -y install wget
apt -y install cmake
apt -y install g++
apt -y install zlib1g-dev
apt -y install libssl-dev Nachdem alle Abhängigkeiten installiert sind, kann man installieren lite-client, Fift, FunC.
Zuerst klonen wir das TON-Repository zusammen mit den Abhängigkeiten. Zur Vereinfachung machen wir alles im Ordner ~/TON.
cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursiveIm Repository befinden sich auch Implementierungen Fift und FunC.
Jetzt sind wir bereit, das Projekt zu erstellen. Der Code des Repositories wurde in den Ordner geklont ~/TON/ton. In ~/TON wir erstellen einen Ordner build und bauen das Projekt darin.
mkdir ~/TON/build
cd ~/TON/build
cmake ../tonDa wir vorhaben, einen Smart Contract zu schreiben, benötigen wir nicht nur lite-client, sondern auch Fift c FunC, daher kompilieren wir alles. Es ist ein langsamer Prozess, also warten wir.
cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target funcDann laden wir die Konfigurationsdatei herunter, die die Daten über den Knoten enthält, mit dem lite-client verbunden werden soll.
wget https://test.ton.org/ton-lite-client-test1.config.jsonWir machen die ersten Anfragen an TON
Jetzt starten wir lite-client.
cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.jsonWenn der Build erfolgreich war, sehen Sie nach dem Start die Protokolle zur Verbindung des Lite-Clients mit dem Knoten.
[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode] conn ready
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] server version is 1.1, capabilities 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] server time is 1582054823 (delta 0)
...Man kann den Befehl ausführen help und sehen, welche Befehle verfügbar sind.
helpLassen Sie uns die Befehle auflisten, die wir in diesem Artikel verwenden werden.
Liste der verfügbaren Befehle:
last Holen Sie sich die letzte Block- und Statusinformation vom Server
sendfile Lädt eine serialisierte Nachricht von und sendet sie an den Server
getaccount [] Lädt den aktuellsten Zustand des angegebenen Kontos; hat das Format [:]
runmethod [] ... Führt die GET-Methode des Kontos mit den angegebenen Parametern auslast erhält den zuletzt erstellten Block vom Server.
sendfile sendet eine Datei mit einer Nachricht an TON, mit diesem Befehl wird der Smart Contract veröffentlicht und Anfragen an ihn gestellt.
getaccount lädt den aktuellen Zustand des Smart Contracts mit der angegebenen Adresse.
runmethod [] führt get-Methoden des Smart Contracts aus. Jetzt sind wir bereit, den Contract selbst zu schreiben.
Implementierung
Idee
Wie bereits erwähnt, handelt es sich bei dem Smart Contract, den wir schreiben, um eine Lotterie.
Und es ist keine Lotterie, bei der man ein Ticket kaufen und eine Stunde, einen Tag oder einen Monat warten muss, sondern eine Sofortlotterie, bei der der Benutzer an die Adresse des Contracts N Gramm, und sofort zurückbekommt 2 * N Gramm oder verliert. Die Gewinnwahrscheinlichkeit setzen wir auf etwa 40 %. Wenn die Gramm für die Auszahlung nicht ausreichen, betrachten wir die Transaktion als Einzahlung.
Es ist wichtig, dass die Einsätze in Echtzeit und in einer verständlichen Form angezeigt werden, damit der Benutzer sofort erkennen kann, ob er gewonnen oder verloren hat. Daher müssen wir eine Website erstellen, die die Einsätze und Ergebnisse direkt aus TON anzeigt.
Schreiben des Smart Contracts
Zur Vereinfachung habe ich eine Hervorhebung für FunC erstellt, das Plugin kann gefunden und im Visual Studio Code installiert werden, falls jemand etwas hinzufügen möchte, habe ich das Plugin öffentlich zur Verfügung gestellt. Außerdem wurde zuvor ein Plugin für die Arbeit mit Fift erstellt, das ebenfalls in VSC gefunden und installiert werden kann.
Lassen Sie uns sofort ein Repository erstellen, in das wir Zwischenstände einpflegen.
Um uns das Leben zu erleichtern, werden wir den Smart Contract schreiben und lokal testen, bis er bereit ist. Erst danach werden wir ihn in TON veröffentlichen.
Der Smart Contract hat zwei externe Methoden, auf die zugegriffen werden kann. Die erste, recv_external() Diese Funktion wird ausgeführt, wenn eine Anfrage an den Contract aus der externen Welt erfolgt, also nicht aus TON, zum Beispiel wenn wir eine Nachricht selbst bilden und sie über den Lite-Client senden. Die zweite, recv_internal() Das ist, wenn innerhalb des TON ein Vertrag auf unseren zugreift. In beiden Fällen können Parameter an die Funktion übergeben werden.
Lassen Sie uns mit einem einfachen Beispiel beginnen, das funktioniert, wenn es veröffentlicht wird, aber keine funktionale Last hat.
() recv_internal(slice in_msg) impure {
;; TODO: Implementierung
}
() recv_external(slice in_msg) impure {
;; TODO: Implementierung
}Hier müssen wir erklären, was sliceist. Alle gespeicherten Daten in der TON Blockchain sind eine Sammlung TVM-Zelle oder einfach Zelle, in einer solchen Zelle können bis zu 1023 Bit Daten und bis zu 4 Links zu anderen Zellen gespeichert werden.
TVM-Zellen-Slice oder slice ist ein Teil einer bestehenden Zelle wird zu ihrer Analyse verwendet, das wird später klar. Das Wichtigste für uns ist, dass wir im Smart Contract slice übergeben können und je nach Art der Nachricht die Daten in recv_external() oder recv_internal().
impure — ein Schlüsselwort, das darauf hinweist, dass die Funktion die Daten des Smart Contracts ändert.
Wir speichern den Code des Vertrags in lottery-code.fc und kompilieren ihn.
~\/TON\/build\/crypto\/func -APSR -o lottery-compiled.fif ~\/TON\/ton\/crypto\/smartcont\/stdlib.fc .\/lottery-code.fc Die Werte der Flags können mit dem Befehl angesehen werden
~\/TON\/build\/crypto\/func -helpWir haben den kompilierten Fift-Assembler-Code in lottery-compiled.fif:
// lottery-compiled.fif
"Asm.fif" include
// automatically generated from `/Users/rajymbekkapisev/TON/ton/crypto/smartcont/stdlib.fc` `./lottery-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
recv_internal PROC:<{
// in_msg
DROP //
}>
recv_external PROC:<{
// in_msg
DROP //
}>
}END>cEs kann lokal ausgeführt werden, dafür bereiten wir die Umgebung vor.
Wir stellen fest, dass in der ersten Zeile Asm.fif, dies ist der in Fift geschriebene Code für den Fift-Assembler.
Da wir den Smart Contract lokal ausführen und testen möchten, erstellen wir eine Datei lottery-test-suite.fif und kopieren den kompilierten Code dorthin, wobei wir die letzte Zeile ersetzen, die den Code des Smart Contracts in eine Konstante schreibt code, um ihn später an die virtuelle Maschine zu übergeben:
"TonUtil.fif" include
"Asm.fif" include
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
recv_internal PROC:<{
\/\/ in_msg
DROP \/\/
}>
recv_external PROC:<{
\/\/ in_msg
DROP \/\/
}>
}END>s constant code
Bisher ist es klar, jetzt fügen wir denselben Code in die Datei ein, den wir zur Ausführung von TVM verwenden werden.
0 tuple 0x076ef1ea , \/\/ magic
0 , 0 , \/\/ actions msg_sents
1570998536 , \/\/ unix_time
1 , 1 , 3 , \/\/ block_lt, trans_lt, rand_seed
0 tuple 100000000000000 , dictnew , , \/\/ verbleibender Saldo
0 , dictnew , \/\/ contract_address, global_config
1 tuple \/\/ wrap to another tuple
constant c7
0 constant recv_internal \/\/ um recv_internal() auszuführen
-1 constant recv_external \/\/ um recv_external() aufzurufenIm c7 schreiben wir den Kontext, also die Daten, mit denen TVM gestartet wird (oder den Status des Netzwerks). Noch während des Wettbewerbs hat einer der Entwickler gezeigt, wie man c7 erstellte, und ich habe es kopiert. In diesem Artikel müssen wir möglicherweise rand_seed ändern. Da es von ihm abhängt, die Zufallszahl zu generieren und unverändert bleibt, wird jedes Mal dieselbe Zahl zurückgegeben.
recv_internal und recv_external Die Konstanten mit den Werten 0 und -1 werden für den Aufruf der entsprechenden Funktionen im Smart Contract verantwortlich sein.
Jetzt sind wir bereit, den ersten Test für unseren leeren Smart Contract zu erstellen. Zur Veranschaulichung werden wir zunächst alle Tests in diese Datei einfügen. lottery-test-suite.fif.
Lass uns eine Variable erstellen storage und sie leeg speichern, Zelledas wird der Speicher des Smart Contracts sein.
message Das ist die Nachricht, die wir dem Smart Contract von außen übermitteln werden. Auch sie machen wir zunächst leer.
Variablen-Speicher
<b b> Speicher !
Variablenmeldung
<b b> Meldung ! Nachdem wir die Konstanten und Variablen vorbereitet haben, starten wir die TVM mit dem Befehl runvmctx und übergeben die erstellten Parameter als Eingabe.
message @
recv_external
code
storage @
c7
runvmctx Am Ende wird es bei uns Zwischencode in Fift.
Jetzt können wir den erzeugten Code ausführen.
export FIFTPATH=~\/TON\/ton\/crypto\/fift\/lib \/\/ einmal ausführen für Bequemlichkeit
~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif Das Programm sollte ohne Fehler arbeiten und wir sehen die Ausführungsprotokolle:
execute SETCP 0
execute DICTPUSHCONST 19 (xC_,1)
execute DICTIGETJMPZ
execute DROP
execute implicit RET
[ 3][t 0][1582281699.325381279][vm.cpp:479] Schritte: 5 Gas: verwendet=304, max=9223372036854775807, Limit=9223372036854775807, Kredit=0Gut, wir haben die erste funktionierende Version des Smart Contracts geschrieben.
Jetzt müssen wir die Funktionalität hinzufügen. Zuerst kümmern wir uns um die Nachrichten, die aus der Außenwelt kommen in recv_external()
Der Entwickler wählt das Nachrichtenformat, das der Vertrag akzeptieren kann.
Aber normalerweise,
- erstens wollen wir unseren Vertrag vor der Außenwelt schützen und sicherstellen, dass nur der Besitzer des Vertrages ihm externe Nachrichten senden kann.
- Zweitens, wenn wir eine gültige Nachricht an TON senden, möchten wir, dass dies genau einmal geschieht, und bei erneuter Sendung derselben Nachricht lehnt der Smart Contract sie ab.
Deshalb werden in fast jedem Vertrag diese beiden Probleme gelöst, da unser Vertrag externe Nachrichten akzeptiert, müssen wir uns auch darum kümmern.
Wir werden das in umgekehrter Reihenfolge angehen. Zuerst lösen wir das Problem mit der Wiederholung; wenn der Vertrag bereits eine solche Nachricht erhalten und verarbeitet hat, wird er sie nicht ein zweites Mal ausführen. Danach lösen wir das Problem, dass nur bestimmte Personen Nachrichten an den Smart Contract senden können.
Es gibt verschiedene Möglichkeiten, das Problem mit wiederholten Nachrichten zu lösen. So werden wir es machen. Im Smart Contract initialisieren wir einen Zähler für empfangene Nachrichten mit einem Anfangswert von 0. In jeder Nachricht fügen wir den aktuellen Zählerwert zum Smart Contract hinzu. Wenn der Zählerwert in der Nachricht nicht mit dem Wert im Smart Contract übereinstimmt, verarbeiten wir ihn nicht; wenn er übereinstimmt, verarbeiten wir ihn und erhöhen den Zähler im Smart Contract um 1.
Wir kehren zurück nach lottery-test-suite.fif und fügen ihm einen zweiten Test hinzu. Wir werden eine falsche Nummer senden, der Code sollte eine Ausnahme werfen. Angenommen, die Vertragsdaten enthalten 166, aber wir senden 165.
<b 166 32 u, b> Speicher!
<b 165 32 u, b> Nachricht!
Nachricht @
recv_external
Code
Speicher @
c7
runvmctx
abbrechen
Exit-Code !
."Exit-Code " exit_code @ . cr
exit_code @ 33 - abort"Test #2 nicht bestanden"Lass uns starten.
~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif Und wir sehen, dass der Test mit einem Fehler ausgeführt wird.
[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 nicht bestanden
[ 1][t 0][1582283084.210941076][fift-main.cpp:196] Fehler beim Interpretieren der Datei `lottery-test-suite.fif`: Fehler beim Interpretieren der eingebetteten Datei `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 nicht bestandenAn diesem Punkt lottery-test-suite.fif sollte wie folgt aussehen .
Jetzt lassen Sie uns die Logik des Zählers im Smart Contract in lottery-code.fc.
() recv_internal(slice in_msg) impure {
;; TODO: Implementierung
}
() recv_external(slice in_msg) impure {
if (slice_empty?(in_msg)) {
return ();
}
int msg_seqno = in_msg~load_uint(32);
var ds = begin_parse(get_data());
int stored_seqno = ds~load_uint(32);
throw_unless(33, msg_seqno == stored_seqno);
}Im slice in_msg enthält die Nachricht, die wir senden.
Das Erste, was wir tun, ist zu überprüfen, ob in der Nachricht Daten vorhanden sind; wenn nicht, verlassen wir einfach die Funktion.
Danach parsen wir die Nachricht. in_msg~load_uint(32) lädt die Zahl 165, 32-Bit unsigned int aus der übergebenen Nachricht.
Dann laden wir 32 Bit aus dem Speicher des Smart Contracts. Wir überprüfen, ob die geladene Zahl mit der übergebenen übereinstimmt; wenn nicht, werfen wir eine Ausnahme. In unserem Fall sollte eine Ausnahme geworfen werden, da wir eine abweichende Zahl übergeben.
Jetzt kompilieren wir.
~\/TON\/build\/crypto\/func -APSR -o lottery-compiled.fif ~\/TON\/ton\/crypto\/smartcont\/stdlib.fc .\/lottery-code.fc Der resultierende Code wird in lottery-test-suite.fif, wobei wir die letzte Zeile nicht vergessen zu ersetzen.
Wir überprüfen, ob der Test besteht:
~\/TON\/build\/crypto\/fift -s lottery-test-suite.fifwir den entsprechenden Commit mit den aktuellen Ergebnissen einsehen.
Wir stellen fest, dass es umständlich ist, den kompilierten Code des Smart Contracts ständig in die Testdateien zu kopieren, daher schreiben wir ein Skript, das den Code für uns in eine Konstante schreibt, und wir binden den kompilierten Code einfach in unsere Tests ein mit "include".
Im Projektordner erstellen wir eine Datei build.sh mit folgendem Inhalt.
#!/bin/bash
~/TON/build/crypto/func -SPA -R -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fcWir machen es ausführbar.
chmod +x .\/build.shJetzt müssen wir nur noch unser Skript ausführen, um den Vertrag zu kompilieren. Aber zusätzlich müssen wir ihn in eine Konstante speichern. codeDeshalb werden wir eine neue Datei erstellen. lotter-compiled-for-test.fif, die wir in die Datei einfügen werden. lottery-test-suite.fif.
Fügen wir den Code in das sh-Skript ein, der einfach die kompilierte Datei in lotter-compiled-for-test.fif kopiert und die letzte Zeile darin ändert.
# copy and change for test
cp lottery-compiled.fif lottery-compiled-for-test.fif
sed '$d' lottery-compiled-for-test.fif > test.fif
rm lottery-compiled-for-test.fif
mv test.fif lottery-compiled-for-test.fif
echo -n "}END>s constant code" >> lottery-compiled-for-test.fifJetzt, um es zu überprüfen, führen wir das entstandene Skript aus und es wird eine Datei generiert. lottery-compiled-for-test.fif, die wir in unser lottery-test-suite.fif
Im lottery-test-suite.fif lösen den Code-Vertrag und fügen die Zeile hinzu "lottery-compiled-for-test.fif" include.
Wir führen die Tests aus, um zu überprüfen, dass sie bestehen.
~\/TON\/build\/crypto\/fift -s lottery-test-suite.fifAusgezeichnet, jetzt erstellen wir eine Datei, um den Testlauf zu automatisieren. test.sh, die zunächst build.sh, und dann die Tests ausführen wird.
touch test.sh
chmod +x test.shInhaltlich schreiben wir
.\/build.sh
echo "nKompilierung abgeschlossenn"
export FIFTPATH=~\/TON\/ton\/crypto\/fift\/lib
~\/TON\/build\/crypto\/fift -s lottery-test-suite.fifLassen Sie es uns test.sh ausführen, um die Funktionalität der Tests zu überprüfen.
chmod +x .\/test.sh
.\/test.shWir überprüfen, dass der Vertrag kompiliert und die Tests ausgeführt werden.
Ausgezeichnet, jetzt bei der Ausführung test.sh wird sofort die Kompilierung und Ausführung der Tests erfolgen. Hier ist der Link zur .
Okay, bevor wir fortfahren, lassen Sie uns noch eine Sache für die Bequemlichkeit tun.
Wir erstellen einen Ordner build in dem wir den kompilierten Vertrag und dessen Klon, der in einer Konstante gespeichert wird, ablegen. lottery-compiled.fif, lottery-compiled-for-test.fifWir erstellen auch einen Ordner, test in dem die Datei mit den Tests gespeichert wird lottery-test-suite.fif und möglicherweise andere Hilfsdateien. .
Lassen Sie uns mit der Entwicklung des Smart Contracts fortfahren.
Nun sollte ein Test folgen, der überprüft, dass eine Nachricht empfangen wird und der Zähler im Speicher aktualisiert wird, wenn wir die richtige Zahl senden. Aber das machen wir später.
Jetzt denken wir darüber nach, welche Datenstruktur und welche Daten im Smart Contract gespeichert werden müssen.
Ich werde alles beschreiben, was wir speichern.
`seqno` 32-Bit ganze positive Zahl, Zähler.
`pubkey` 256-Bit ganze positive Zahl, öffentlicher Schlüssel, mit dem wir die Signatur der extern gesendeten Nachricht verifizieren werden, siehe unten.
`order_seqno` 32-Bit ganze positive Zahl, speichert den Zähler der Anzahl der Einsätze.
`number_of_wins` 32-Bit ganze positive Zahl, speichert die Anzahl der gewonnenen Einsätze.
`incoming_amount` Datentyp Gram (die ersten 4 Bits geben die Länge an), speichert die Gesamtanzahl der Gramm, die an den Vertrag gesendet wurden.
`outgoing_amount` Gesamtanzahl der Gramm, die an die Gewinner gesendet wurden.
`owner_wc` Nummer des Workchains, 32-Bit (in einigen Quellen steht, dass es 8-Bit ist) ganze Zahl. Momentan gibt es nur zwei -1 und 0.
`owner_account_id` 256-Bit ganze positive Zahl, Adresse des Vertrags im aktuellen Workchain.
`orders` Dictionary-Variable, speichert die letzten zwanzig Einsätze. Als nächstes müssen zwei Funktionen geschrieben werden. Nennen wir die erste pack_state(), die die Daten für die spätere Speicherung im Smart Contract verpackt. Die zweite, nennen wir sie unpack_state() , wird die Daten aus dem Speicher lesen und zurückgeben.
_ pack_state(int seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) inline_ref {
return begin_cell()
.store_uint(seqno, 32)
.store_uint(pubkey, 256)
.store_uint(order_seqno, 32)
.store_uint(number_of_wins, 32)
.store_grams(incoming_amount)
.store_grams(outgoing_amount)
.store_int(owner_wc, 32)
.store_uint(owner_account_id, 256)
.store_dict(orders)
.end_cell();
}
_ unpack_state() inline_ref {
var ds = begin_parse(get_data());
var unpacked = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_uint(32), ds~load_grams(), ds~load_grams(), ds~load_int(32), ds~load_uint(256), ds~load_dict());
ds.end_parse();
return unpacked;
}Fügen Sie diese beiden Funktionen zu Beginn des Smart Contracts hinzu. Es wird ein Zwischenergebnis entstehen.
Um die Daten zu speichern, muss die eingebaute Funktion set_data() aufgerufen werden, und sie wird die Daten aus pack_state() im Speicher des Smart Contracts speichern.
cell packed_state = pack_state(arg_1, .., arg_n);
set_data(packed_state);Jetzt, da wir bequeme Funktionen zum Schreiben und Lesen von Daten haben, können wir weitermachen.
Wir müssen überprüfen, ob die eingehende externe Nachricht vom Eigentümer des Vertrags unterzeichnet wurde (oder von einem anderen Benutzer, der Zugriff auf den privaten Schlüssel hat).
Beim Veröffentlichen eines Smart Contracts können wir ihn mit den benötigten Daten im Speicher initialisieren, die für zukünftige Verwendung gespeichert werden. Wir werden dort den öffentlichen Schlüssel ablegen, um zu überprüfen, dass die Signatur der eingehenden Nachricht mit dem entsprechenden privaten Schlüssel erstellt wurde.
Bevor wir fortfahren, erstellen wir einen privaten Schlüssel und speichern ihn in test/keys/owner.pk. Dazu starten wir Fift im interaktiven Modus und führen vier Befehle aus.
`newkeypair` generiert ein Schlüsselpaar und legt es auf den Stapel.
`drop` entfernt das oberste Element (in diesem Fall den öffentlichen Schlüssel) vom Stapel.
`.s` zeigt einfach an, was derzeit auf dem Stapel liegt.
`"owner.pk" B>file` speichert den privaten Schlüssel in einer Datei mit dem Namen `owner.pk`.
`bye` beendet die Arbeit mit Fift. Wir erstellen einen Ordner keys im Verzeichnis test und dort werden wir den privaten Schlüssel speichern.
mkdir test/keys
cd test/keys
~/TON/build/crypto/fift -i
newkeypair
ok
.s
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128
drop
ok
"owner.pk" B>file
ok
byeWir sehen in dem aktuellen Verzeichnis die Datei owner.pk.
Wir entfernen den öffentlichen Schlüssel vom Stapel. Wenn wir ihn benötigen, können wir ihn aus dem privaten Schlüssel abrufen.
Jetzt müssen wir die Signatur überprüfen. Beginnen wir mit einem Test. Zuerst lesen wir den privaten Schlüssel aus der Datei mit Hilfe der Funktion file>B und speichern ihn in der Variablen owner_private_key, dann konvertieren wir mit der Funktion priv>pub den privaten Schlüssel in den öffentlichen und speichern das Ergebnis in owner_public_key.
variable owner_private_key
variable owner_public_key
"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !Wir benötigen beide Schlüssel.
Wir initialisieren den Speicher des Smart Contracts mit willkürlichen Daten in derselben Reihenfolge wie in der Funktion pack_state()und speichern in der Variablen storage.
variable owner_private_key
variable owner_public_key
variable orders
variable owner_wc
variable owner_account_id
".\/keys\/owner.pk" Datei>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !
dictnew orders !
0 owner_wc !
0 owner_account_id !
<b 0 32 u, owner_public_key @ b, gram, owner_wc i, owner_account_id 256 orders dict, b> Speicher!Dann stellen wir eine signierte Nachricht zusammen, die nur die Signatur und den Zählerstand enthält.
Zuerst erstellen wir die Daten, die wir übermitteln möchten, dann signieren wir sie mit dem privaten Schlüssel und schließlich formen wir die signierte Nachricht.
variable nachricht_zum_unterschreiben
variable nachricht_zum_versenden
variable signatur
<b 0 32 u, b> nachricht_zum_unterschreiben !
nachricht_zum_unterschreiben @ hashu privater_besitzer_schlüssel @ ed25519_sign_uint signatur !
<b signature @ b, 0 32 u, b> <s ! Letztendlich ist die Nachricht, die wir an den Smart Contract senden, in der Variablen message_to_send, bezüglich der Funktionen hashu, ed25519_sign_uint kann gelesen werden .
Und um den Test erneut auszuführen, rufen wir wieder auf.
message_to_send @
recv_external
code
storage @
c7
runvmctxdie Datei mit den Tests in diesem Stadium aussehen.
Lass uns den Test starten und er wird fehlschlagen, daher verändern wir den Smart Contract, damit er Nachrichten in diesem Format empfangen und die Signatur überprüfen kann.
Zuerst lesen wir aus der Nachricht 512 Bit der Signatur und speichern sie in einer Variablen, dann lesen wir 32 Bit der Zählervariablen.
Da wir eine Funktion zum Auslesen von Daten aus dem Smart-Contract-Speicher haben, werden wir sie verwenden.
Dann überprüfen wir den Zähler, der mit dem Speicher übergeben wurde, und die Signatur. Wenn etwas nicht übereinstimmt, werfen wir eine Ausnahme mit dem entsprechenden Code.
var signature = in_msg~load_bits(512);
var message = in_msg;
int msg_seqno = message~load_uint(32);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, pubkey));Entsprechender Commit .
Wir werden die Tests starten und sehen, dass der zweite Test fehlschlägt. Aus zwei Gründen, Unzulänglichkeit der Bits in der Nachricht und Unzulänglichkeit der Bits im Speicher, weshalb der Code beim Parsen fehlschlägt. Wir müssen die Signatur der Nachricht hinzufügen, die wir senden, und den Speicher aus dem letzten Test kopieren.
Im zweiten Test fügen wir die Signatur der Nachricht hinzu und ändern den Speicher des Smart Contracts. Wie die Datei mit den Tests im Moment aussieht.
Wir schreiben einen vierten Test, in dem wir eine Nachricht senden, die mit einem fremden privaten Schlüssel signiert ist. Wir erstellen einen weiteren privaten Schlüssel und speichern ihn in einer Datei. not-owner.pk. Mit diesem privaten Schlüssel werden wir die Nachricht signieren. Wir werden die Tests starten und sicherstellen, dass alle Tests bestehen. bis zum heutigen Datum.
Jetzt können wir endlich zur Umsetzung der Logik des Smart Contracts übergehen.
Im recv_external() Wir werden zwei Arten von Nachrichten empfangen.
Da unser Vertrag die Verluste der Spieler akkumulieren wird, müssen dieses Geld an den Ersteller der Lotterie überwiesen werden. Die Adresse des Wallets des Lotterieerstellers wird beim Erstellen des Vertrags im Speicher aufgezeichnet.
Für alle Fälle benötigen wir die Möglichkeit, die Adresse zu ändern, an die die verlorenen Grams gesendet werden. Außerdem sollten wir in der Lage sein, Grams von der Lotterie an die Adresse des Eigentümers zu senden.
Fangen wir mit dem ersten an. Lassen Sie uns zunächst einen Test schreiben, der überprüft, dass nach dem Senden einer Nachricht der Smart Contract die neue Adresse im Speicher gespeichert hat. Wir beachten, dass wir neben dem Zähler und der neuen Adresse auch Aktion eine 7-Bit-ganze positive Zahl übergeben, je nachdem werden wir entscheiden, wie wir die Nachricht im Smart Contract verarbeiten.
<b 0 32 u, 1 @ 7 new_owner_wc i, new_owner_account_id 256 b> Nachricht zur Signierung !Im Test sieht man, wie die Deserialisierung des Speichers des Smart Contracts erfolgt. storage In Fift. Die Deserialisierung der Variablen ist in der Dokumentation zu Fift beschrieben.
mit dem Hinzufügen eines Tests.
Wir führen den Test aus und stellen sicher, dass er fehlschlägt. Jetzt fügen wir die Logik zur Änderung der Adresse des Lotteriebesitzers hinzu.
Im Smart Contract setzen wir das Parsen fort message, lesen wir in Aktion. Lassen Sie uns daran erinnern, dass wir zwei Aktion: die Adresse zu ändern und Grams zu senden.
Dann lesen wir die neue Adresse des Vertragseigentümers und speichern sie im Speicher.
Wir führen die Tests aus und sehen, dass der dritte Test fehlschlägt. Er schlägt fehl, weil der Vertrag jetzt zusätzlich 7 Bits aus der Nachricht parst, die im Test fehlen. Lassen Sie uns eine nicht existierende in die Nachricht einfügen Aktion. Lassen Sie uns die Tests erneut durchführen und sehen, dass alle bestehen. Commit zu den Änderungen. Großartig.
Jetzt schreiben wir die Logik zum Senden der angegebenen Menge an Grams an die zuvor gespeicherte Adresse.
Zuerst werden wir einen Test schreiben. Wir werden zwei Tests schreiben: einen, wenn nicht genug Guthaben vorhanden ist, und einen, wenn alles erfolgreich verlaufen sollte. Die Tests können in diesem Commit eingesehen werden .
Jetzt schreiben wir den Code weiter. Zuerst schreiben wir zwei Hilfsmethoden. Die erste ist eine Getter-Methode, um den aktuellen Kontostand des Smart Contracts zu erfahren.
int balance() inline_ref method_id {
return get_balance().pair_first();
}Und die zweite zum Senden von Grams an einen anderen Smart Contract. Diese Methode habe ich vollständig aus einem anderen Smart Contract kopiert.
() send_grams(int wc, int addr, int grams) impure {
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
cell msg = begin_cell()
;; .store_uint(0, 1) ;; 0 <= Formatindikator int_msg_info$0
;; .store_uint(1, 1) ;; 1 <= ihr ist deaktiviert
;; .store_uint(1, 1) ;; 1 <= Bounce = true
;; .store_uint(0, 1) ;; 0 <= bounced = false
;; .store_uint(4, 5) ;; 00100 <= Adressenvorgaben, Anycast = false, 8-Bit-Arbeitskette
.store_uint (196, 9)
.store_int(wc, 8)
.store_uint(addr, 256)
.store_grams(grams)
.store_uint(0, 107) ;; 106 Nullen + 0 als Indikator, dass es keine Zelle mit den Daten gibt.
.end_cell();
send_raw_message(msg, 3); ;; Modus, 2 zum Ignorieren von Fehlern, 1 für Sender zahlt Gebühren, 64 für die Rückgabe des eingehenden Nachrichtenwerts
}Fügen wir diese beiden Methoden in den Smart Contract ein und schreiben die Logik. Zuerst parsen wir die Anzahl der Grams aus der Nachricht. Danach überprüfen wir das Guthaben, und wenn es nicht ausreicht, werfen wir eine Ausnahme. Wenn alles gut ist, senden wir die Grams an die gespeicherte Adresse und aktualisieren den Zähler.
int amount_to_send = message~load_grams();
throw_if(36, amount_to_send + 500000000 > balance());
accept_message();
send_grams(owner_wc, owner_account_id, amount_to_send);
set_data(pack_state(stored_seqno + 1, pubkey, order_seqno, number_of_wins, incoming_amount, outgoing_amount, owner_wc, owner_account_id, orders));so sieht der Smart Contract momentan aus. Lassen Sie uns die Tests durchführen und sicherstellen, dass sie bestehen.
Übrigens wird für jede verarbeitete Nachricht von einem Smart Contract eine Gebühr fällig. Damit der Smart Contract die Anfrage ausführt, müssen nach den grundlegenden Überprüfungen accept_message().
Jetzt kümmern wir uns um die internen Nachrichten. Tatsächlich werden wir nur Gramm empfangen und dem Spieler den doppelten Betrag im Gewinn und ein Drittel an den Besitzer bei Verlust zurücksenden.
Zuerst schreiben wir einen einfachen Test. Dazu benötigen wir eine Testadresse des Smart Contracts, von der aus wir, als ob wir Gramm an den Smart Contract senden.
Die Adresse des Smart Contracts besteht aus zwei Zahlen, einer 32-Bit-Ganzzahl, die für die Workchain verantwortlich ist, und einer 256-Bit-Ganzzahl, die eine nicht negative eindeutige Kontonummer in dieser Workchain darstellt. Zum Beispiel -1 und 12345, diese Adresse speichern wir in einer Datei.
Ich habe die Funktion zum Speichern der Adresse aus .
// ( wc addr fname -- ) Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-addressLass uns die Funktionsweise der Funktion untersuchen, das wird das Verständnis der Funktionsweise von Fift erleichtern. Wir starten Fift im interaktiven Modus.
~\/TON\/build\/crypto\/fift -i Zuerst legen wir -1, 12345 und den Namen der zukünftigen Datei "sender.addr" auf den Stack:
-1 12345 "sender.addr" Im nächsten Schritt wird die Funktion -rot, die den Stack verschiebt, so dass die eindeutige Nummer des Smart Contracts oben auf dem Stack erscheint:
"sender.addr" -1 12345256 u>B konvertiert die 256-Bit nicht negative Ganzzahl in Bytes.
"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039swap tauscht die zwei obersten Elemente des Stacks.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -132 i>B konvertiert die 32-Bit-Ganzzahl in Bytes.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFFB+ verbindet zwei Byte-Sequenzen.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFFWieder swap.
BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" Und schließlich wird die Speicherung der Bytes in die Datei ausgeführt B>file. Danach ist unser Stack leer. Wir stoppen Fift. Im aktuellen Ordner wurde die Datei sender.addr. Wir verschieben die Datei in den erstellten Ordner test\/addresses\/.
Wir schreiben einen einfachen Test, der Gramm an den Smart Contract sendet. .
Jetzt kümmern wir uns um die Logik der Lotterie.
Das erste, was wir tun, ist die Überprüfung der Nachricht bounced oder nicht, wenn bounced, ignorieren wir sie. bounced Das bedeutet, dass der Vertrag Gramm zurückgeben wird, wenn ein Fehler auftritt. Wir werden keine Gramm zurückgeben, wenn plötzlich ein Fehler auftritt.
Wir überprüfen das Guthaben; wenn es weniger als ein halbes Gramm ist, akzeptieren wir einfach die Nachricht und ignorieren sie.
Wir parsen die Adresse des Smart Contracts, von dem die Nachricht kam.
Wir lesen die Daten aus dem Speicher und entfernen dann alte Einsätze aus der Historie, wenn es mehr als zwanzig sind. Zur Vereinfachung habe ich drei zusätzliche Funktionen geschrieben. pack_order(), unpack_order(), remove_old_orders().
Als nächstes prüfen wir, ob das Guthaben für die Auszahlung nicht ausreicht. Wenn ja, betrachten wir dies nicht als Wette, sondern als Aufladung und speichern die Aufladung in orders.
Weiter geht es mit dem Wesentlichen des Smart Contracts.
Zuerst, wenn der Spieler verloren hat, speichern wir ihn in der Wetthistorie und wenn der Betrag mehr als 3 Gramm beträgt, senden wir 1/3 an den Besitzer des Smart Contracts.
Wenn der Spieler gewonnen hat, senden wir den doppelte Betrag an die Adresse des Spielers und speichern die Informationen zur Wette in der Historie.
() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
int flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
if (flags & 1) { ;; ignore bounced
return ();
}
if (order_amount < 500000000) { ;; just receive grams without changing state
return ();
}
slice src_addr_slice = cs~load_msg_addr();
(int src_wc, int src_addr) = parse_std_addr(src_addr_slice);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
orders = remove_old_orders(orders, order_seqno);
if (balance() = 4) {
builder order = pack_order(order_seqno, 3, now(), order_amount, src_wc, src_addr);
orders~udict_set_builder(32, order_seqno, order);
set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
if (order_amount > 3000000000) {
send_grams(owner_wc, owner_account_id, order_amount / 3);
}
return ();
}
send_grams(src_wc, src_addr, 2 * order_amount);
builder order = pack_order(order_seqno, 2, now(), order_amount, src_wc, src_addr);
orders~udict_set_builder(32, order_seqno, order);
set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins + 1, incoming_amount, outgoing_amount + 2 * order_amount, owner_wc, owner_account_id, orders));
}Das ist alles. .
Jetzt bleibt es einfach, wir machen die Get-Methoden, um aus der externen Welt Informationen über den Status des Contracts zu erhalten (tatsächlich die Daten aus dem Speicher des Smart Contracts abzurufen).
. Darauf, wie man Informationen über den Smart Contract erhält, werden wir weiter unten schreiben.
Ich habe auch vergessen, den Code hinzuzufügen, der die erste Anfrage verarbeitet, die beim Veröffentlichen des Smart Contracts gestellt wird. . Und noch den Fehler beim Senden von 1/3 des Betrags an das Konto des Inhabers.
Dann bleibt nur noch der Smart Contract zu veröffentlichen. Wir erstellen einen Ordner requests.
Ich habe den Veröffentlichungscode als Grundlage genommen der im offiziellen Repository.
Was man beachten sollte: Wir erstellen einen Speicher für den Smart Contract und eine Eingabemeldung. Danach wird die Adresse des Smart Contracts generiert, das heißt, die Adresse ist bereits vor der Veröffentlichung in TON bekannt. Dann muss eine bestimmte Menge an Gramm an diese Adresse gesendet werden, und erst danach muss die Datei mit dem Smart Contract selbst gesendet werden, da das Netzwerk eine Gebühr für die Speicherung des Smart Contracts und die darin durchgeführten Operationen erhebt (Validatoren, die Smart Contracts speichern und ausführen). .
Dann führen wir den Veröffentlichungscode aus und erhalten lottery-query.boc die Datei und die Adresse des Smart Contracts.
~/TON/build/crypto/fift -s requests/new-lottery.fif 0Vergessen Sie nicht, die generierten Dateien zu speichern: lottery-query.boc, lottery.addr, lottery.pk.
Unter anderem sehen wir in den Ausführungslogs die Adresse des Smart Contracts.
new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a
(Speichere Adresse in Datei lottery.addr)
Nicht rückzahlbare Adresse (zum Initialisieren): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Rückzahlbare Adresse (für späteren Zugriff): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFYAus Interesse machen wir eine Anfrage an TON
$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydUnd wir sehen, dass das Konto mit dieser Adresse leer ist.
Der Kontostand ist leerWir senden an die Adresse 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gramm und führen nach ein paar Sekunden denselben Befehl aus. Zum Senden von Gramm verwende ich , und Testgramme kann man von jemandem im Chat erbitten, über den ich am Ende des Artikels sprechen werde.
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydMan sieht, dass im Netzwerk ein nicht initialisierter (state:account_uninit) Smart Contract mit dieser Adresse und einem Kontostand von 1.000.000.000 Nanogramm erschienen ist.
Kontostatus ist (Konto
addr:(addr_std
anycast:nothing workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
speicher_stat:(speicher_info
verwendet:(speicher_verwendet
zellen:(var_uint len:1 value:1)
bits:(var_uint len:1 value:103)
öffentliche_zellen:(var_uint len:0 value:0)) letzte_bezahlung:1583257959
fällige_zahlung:nothing)
speicher:(konto_speicher letzte_trans_lt:3825478000002
saldo:(währungen
gramm:(nanogramm
menge:(var_uint len:4 value:2000000000))
andere:(extra_währungen
dict:hme_empty))
zustand:konto_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
letzte Transaktion lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
Konto saldo ist 2000000000ngJetzt veröffentlichen wir den Smart Contract. Starten wir den Lite-Client und führen wir es aus.
> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] sende Abfrage aus der Datei lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query] externer Nachrichtenstatus ist 1 Überprüfen wir, ob der Vertrag veröffentlicht wurde.
> letzte
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydUnter anderem erhalten wir.
speicher:(konto_speicher letzte_trans_lt:3825499000002
saldo:(währungen
gramm:(nanogramm
menge:(var_uint len:4 value:1987150999))
andere:(extra_währungen
dict:hme_empty))
zustand:(konto_aktivWir sehen, dass konto_aktiv.
Entsprechender Commit mit Änderungen .
Jetzt erstellen wir Anfragen zur Interaktion mit dem Smart Contract.
Genauer gesagt, lassen wir die erste zur Änderung der Adresse als eigenständige Arbeit und machen die zweite für das Senden von Gramm an die Adresse des Besitzers. Im Grunde müssen wir dasselbe tun wie im Test zum Senden von Gramm.
Diese Nachricht werden wir an den Smart Contract senden, wo msg_seqno 165, Aktion 2 und 9,5 Gramm zum Senden.
<b 165 32 u, 2 7 9500000000 gram, b>Wir dürfen nicht vergessen, die Nachricht mit dem privaten Schlüssel zu signieren lottery.pk, der zuvor bei der Erstellung des Smart Contracts generiert wurde. .
Wir erhalten Informationen aus dem Smart Contract über die Get-Methoden
Jetzt sehen wir uns an, wie man die Get-Methoden des Smart Contracts ausführt.
Starten lite-client und wir führen die Get-Methoden aus, die wir geschrieben haben.
$ .\/lite-client\/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
Argumente: [ 104128 ]
Ergebnis: [ 64633878952 ]
...Im result enthält den Wert, den die Funktion zurückgibt balance() aus unserem Smart Contract.
Dasselbe führen wir auch für mehrere Methoden aus.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
Argumente: [ 77871 ]
Ergebnis: [ 1 ] Fragen wir die Wettgeschichte an.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
Argumente: [ 67442 ]
Ergebnis: [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] Wir werden lite-client und GET-Methoden verwenden, um Informationen über den Smart Contract auf der Website anzuzeigen.
Wir zeigen die Daten des Smart Contracts auf der Website an.
Ich habe eine einfache Website in Python geschrieben, um die Daten aus dem Smart Contract in einer benutzerfreundlichen Form darzustellen. Hier werde ich nicht im Detail darauf eingehen und die Website veröffentlichen. .
Die Anfragen an TON erfolgen aus Python mit lite-client. Zur Vereinfachung wird die Website in Docker verpackt und auf Google Cloud veröffentlicht. .
Wir probieren es aus
Jetzt versuchen wir, von 40 Grams zu senden. Und wir machen ein paar Wetten zur Veranschaulichung. Wir sehen, dass die Website die Wettgeschichte, den aktuellen Gewinnprozentsatz und andere nützliche Informationen anzeigt.
, dass wir die erste gewonnen und die zweite verloren haben.
Nachwort
Der Artikel ist viel länger geworden, als ich angenommen hatte. Vielleicht hätte man ihn kürzer fassen können, oder vielleicht ist er gerade richtig für jemanden, der nichts über TON weiß und einen nicht ganz einfachen Smart Contract mit Interaktion schreiben und veröffentlichen möchte. Möglicherweise hätte man einige Dinge einfacher erklären können.
Es ist möglich, dass einige Aspekte der Implementierung effizienter und eleganter gestaltet werden könnten, aber dann würde es noch mehr Zeit in Anspruch nehmen, den Artikel vorzubereiten. Auch ist es möglich, dass ich irgendwo Fehler gemacht habe oder etwas nicht verstanden habe. Daher sollte man sich, wenn man etwas Ernsthaftes macht, auf die offizielle Dokumentation oder das offizielle Repository mit dem TON-Code stützen.
Es ist zu beachten, dass, da sich TON noch in aktiver Entwicklungsphase befindet, Änderungen auftreten können, die einen der Schritte in diesem Artikel brechen (was bereits passiert ist, während ich schrieb, habe ich es schon korrigiert), aber der allgemeine Ansatz wird sich wahrscheinlich nicht ändern.
Ich werde nicht über die Zukunft von TON spekulieren. Möglicherweise wird die Plattform etwas Größeres, und es könnte sich lohnen, jetzt Zeit für ihre Erforschung zu investieren und mit eigenen Produkten schon jetzt eine Nische einzunehmen.
Es gibt auch Libra von Facebook, deren potenzielle Benutzerbasis größer ist als die von TON. Ich weiß fast nichts über Libra, aber laut dem Forum ist dort die Aktivität deutlich höher als in der TON-Community. Obwohl die Entwickler und die TON-Community eher wie eine Underground-Bewegung erscheinen, was ebenfalls cool ist.
Links
- Die offizielle Dokumentation zu TON:
- Das offizielle Repository von TON:
- Die offizielle Geldbörse für verschiedene Plattformen:
- Das Repository des Smart Contracts aus diesem Artikel:
- Link zur Website des Smart Contracts:
- Repository für die Erweiterung für Visual Studio Code für FunC:
- Der Chat über TON in Telegram, der sehr geholfen hat, sich anfangs zurechtzufinden. Ich denke, es wäre nicht falsch zu sagen, dass dort alle sind, die irgendetwas für TON geschrieben haben. Dort kann man auch nach Testguthaben fragen.
- Ein weiterer Chat über TON, in dem ich nützliche Informationen gefunden habe:
- Erste Phase des Wettbewerbs:
- Zweite Phase des Wettbewerbs:
Quelle: habr.com
