Wie man einen Smart Contract in TON schreibt und veröffentlicht
Worum geht es in diesem Artikel?
In diesem Artikel teile ich meine Erfahrungen aus dem ersten (von zwei) Telegram-Wettbewerb im Bereich Blockchain, bei dem ich nicht den ersten Platz belegt habe. Ich möchte meine Erkenntnisse festhalten, damit sie nicht in Vergessenheit geraten und vielleicht jemandem helfen.
Da ich keinen abstrakten Code schreiben wollte, habe ich für diesen Artikel einen Smart Contract für eine Sofortlotterie sowie eine Website erstellt, die die Daten des Smart Contracts direkt aus TON ohne Zwischenablage anzeigt.
Der Artikel wird für diejenigen nützlich sein, 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 und der Interaktion mit ihm durchgehen und eine Website schreiben, um Daten abzurufen und zu veröffentlichen.
Über die Teilnahme am Wettbewerb
Im Oktober letzten Jahres hat Telegram einen Wettbewerb im Bereich Blockchain mit neuen Sprachen angekündigt. Fift und FunC. Es war erforderlich, aus fünf vorgeschlagenen Smart Contracts auszuwählen. Ich dachte, es wäre nicht schlecht, sich mit etwas Ungewöhnlichem zu beschäftigen, die Sprache zu lernen und etwas zu schaffen, auch wenn ich in Zukunft vielleicht nichts mehr schreiben werde. Außerdem ist das Thema ständig in aller Munde.
Ich sollte erwähnen, dass ich keine Erfahrung in der Entwicklung von Smart Contracts hatte.
Ich hatte geplant, bis zum Ende teilzunehmen, solange es möglich ist, und danach einen Überblicksartikel zu schreiben, aber ich bin gleich bei der ersten Runde gescheitert. Ich mit Multi-Signatur für FunC geschrieben, und sie hat insgesamt funktioniert. Ich habe als Grundlage genommen .
Zu diesem Zeitpunkt dachte ich, das reicht definitiv aus, um zumindest einen Preis zu gewinnen. Am Ende wurden etwa 40 von 60 Teilnehmern prämiert, und ich war nicht darunter. Insgesamt ist das nicht schlimm, aber eine Sache hat mich etwas gestresst. Zu dem Zeitpunkt, als die Ergebnisse der Überprüfungen bekannt gegeben wurden, gab es keine Tests für meinen Contract, ich habe die Teilnehmer im Chat gefragt, ob es noch jemanden gibt, der ihn nicht hat, aber es gab keinen.
Offensichtlich haben die Richter, nachdem sie zwei Tage auf meine Nachrichten geachtet hatten, einen Kommentar veröffentlicht. Ich habe nicht verstanden, ob sie meinen Smart Contract während der Beurteilung versehentlich übersehen haben oder ob sie einfach dachten, dass er so schlecht ist, dass er keinen Kommentar benötigt. Ich stellte eine Frage auf der Seite, aber erhielt keine Antwort. Obwohl es kein Geheimnis darüber gibt, wer gerichtet hat, hielt ich es für überflüssig, persönliche Nachrichten zu schreiben.
Es wurde viel Zeit investiert, um alles zu verstehen, daher wurde beschlossen, einen Artikel zu schreiben. Da die Informationen bislang noch begrenzt sind, wird der Artikel dazu beitragen, allen Interessierten Zeit zu sparen.
Das Konzept der Smart Contracts in TON
Bevor ich etwas schreibe, muss ich mir überlegen, wie ich dieses Thema angehen soll. Daher werde ich jetzt erklären, aus welchen Komponenten das System besteht. Genauer gesagt, welche Teile man kennen muss, um zumindest einen funktionierenden Vertrag zu schreiben.
Wir konzentrieren uns auf das Schreiben eines Smart Contracts und die Arbeit mit TON Virtual Machine (TVM), Fift und FunC, daher ähnelt der Artikel mehr der Beschreibung der Entwicklung eines normalen Programms. Wir werden hier nicht auf die Funktionsweise der Plattform selbst eingehen.
Allgemein über die Funktionsweise von TVM und die Sprache Fift Es gibt eine gute offizielle Dokumentation. Während der Teilnahme an dem Wettbewerb und jetzt beim Schreiben des aktuellen Vertrags habe ich oft darauf zurückgegriffen.
Die Hauptsprache, in der Smart Contracts geschrieben werden, ist FunC. Aktuell gibt es keine Dokumentation darüber, daher muss man, um etwas zu schreiben, Beispiele von Smart Contracts aus dem offiziellen Repository und die dortige Implementierung studieren, außerdem kann man sich Beispiele von Smart Contracts aus den vergangenen zwei Wettbewerben ansehen. Die Links befinden sich 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 bleibt zu veröffentlichen. Dazu müssen wir eine Funktion in Fiftschreiben, die den Code des Smart Contracts und einige andere Parameter als Eingabe nimmt, und als Ausgabe erhalten wir eine Datei mit der Erweiterung .boc (was "bag of cells" bedeutet), und je nachdem, wie wir es schreiben, einen privaten Schlüssel und eine Adresse, die auf Basis des Codes des Smart Contracts generiert wird. An die Adresse des Smart Contracts, die noch nicht veröffentlicht ist, können bereits Grammys gesendet werden.
Um den Smart Contract in TON zu veröffentlichen, benötigt man .boc Die Datei muss über einen Light-Client an die Blockchain gesendet werden (darüber sprechen wir weiter unten). Doch bevor man veröffentlicht, muss man Gramm auf die generierte Adresse übertragen, andernfalls wird der Smart Contract nicht veröffentlicht. Nach der Veröffentlichung kann man mit dem Smart Contract interagieren, indem man Nachrichten von außen (zum Beispiel über den Light-Client) oder von innen (zum Beispiel kann ein Smart Contract einem anderen innerhalb von TON eine Nachricht senden) sendet.
Nachdem wir verstanden haben, wie der Code veröffentlicht wird, wird alles einfacher. Wir wissen ungefähr, was wir schreiben wollen und wie unser Programm funktionieren wird. Während des Schreibens suchen wir, wie es bereits in bestehenden Smart Contracts umgesetzt ist, oder werfen einen Blick in den Implementierungscode. Fift und FunC in dem offiziellen Repository oder sehen in die offizielle Dokumentation.
Sehr oft habe ich nach Schlüsselwörtern im Telegram-Chat gesucht, wo alle Teilnehmer des Wettbewerbs sowie Mitarbeiter von Telegram versammelt waren. Es stellte sich heraus, dass während des Wettbewerbs alle dort zusammengekommen sind und Fift sowie FunC diskutiert haben. Der Link befindet sich am Ende des Artikels.
Es ist an der 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 gemacht und in einer sauberen Ubuntu 18.04 LTS auf Docker überprüft.
Das erste, was zu tun ist, ist das Herunterladen und Installieren von lite-client , mit dem man Anfragen an TON senden kann.
Die Anleitung auf der offiziellen Website beschreibt den Installationsprozess recht detailliert und verständlich und lässt einige Details aus. Hier folgen wir der Anleitung und installieren gleichzeitig die fehlenden Abhängigkeiten. Ich habe nicht jedes Projekt selbst kompiliert, sondern aus dem offiziellen Ubuntu-Repository installiert (unter MacOS habe ich verwendet) 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, können wir lite-client, Fift, FunC.
Zunächst klonen wir das TON-Repository zusammen mit den Abhängigkeiten. Zur Bequemlichkeit erledigen wir alles im Ordner ~/TON.
cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursiveIm Repository sind auch die Implementierungen gespeichert. Fift und FunC.
Nun sind wir bereit, das Projekt zu erstellen. Der Code des Repositories wurde im Ordner ~/TON/tonIn ~/TON erstellt einen Ordner build und bauen darin das Projekt.
mkdir ~/TON/build
cd ~/TON/build
cmake ../tonDa wir beabsichtigen, einen Smart Contract zu schreiben, benötigen wir nicht nur lite-client, sondern auch Fift mit FunC, daher kompilieren wir alles. Es ist kein schneller Prozess, also warten wir.
cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target funcLaden Sie als Nächstes 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 das Protokoll der Verbindung des Lite-Clients mit dem Knoten.
[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode] conn bereit
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] Serverversion ist 1.1, Fähigkeiten 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] Serverzeit ist 1582054823 (Delta 0)
...Sie können 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 letzte Block- und Statusinformationen vom Server
sendfile Laden Sie eine serialisierte Nachricht aus und senden Sie sie an den Server
getaccount [] Lädt den aktuellsten Zustand des angegebenen Kontos; ist im [:] Format
runmethod [] ... Führt die GET-Methode des Kontos mit angegebenen Parametern auslast erhält den zuletzt erstellten Block vom Server.
sendfile sendet die Datei mit der Nachricht an TON; genau 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 die Get-Methoden des Smart Contracts aus. Jetzt sind wir bereit, den Vertrag selbst zu schreiben.
Implementierung
Die Idee
Wie bereits erwähnt, ist der Smart Contract, den wir schreiben, eine Lotterie.
Es handelt sich dabei nicht um eine Lotterie, bei der man ein Ticket kaufen und eine Stunde, einen Tag oder einen Monat warten muss, sondern um eine sofortige, bei der der Benutzer einen Betrag an die Adresse des Vertrags überweist 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 benutzerfreundlichen 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 die Code-Hervorhebung für FunC erstellt; das Plugin kann im Visual Studio Code gesucht und installiert werden. Falls jemand etwas hinzufügen möchte, habe ich das Plugin öffentlich zugänglich gemacht. 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 dem wir die Zwischenstände committen.
Um uns das Leben zu erleichtern, werden wir den Smart Contract schreiben und lokal testen, bis er bereit ist. Erst dann veröffentlichen wir ihn im TON.
Ein Smart Contract hat zwei externe Methoden, auf die zugegriffen werden kann. Die erste, recv_external() wird ausgeführt, wenn eine Anfrage an den Vertrag aus der externen Welt erfolgt, also nicht aus TON, zum Beispiel wenn wir selbst eine Nachricht formulieren und sie über den Lite-Client senden. Die zweite, recv_internal() ist, wenn innerhalb von TON ein anderer 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 funktionieren wird, wenn es veröffentlicht wird, aber keine funktionale Last hat.
() recv_internal(slice in_msg) impure {
;; TODO: implementation
}
() recv_external(slice in_msg) impure {
;; TODO: implementation
}Hier muss erklärt werden, was sliceist. Alle gespeicherten Daten in der TON Blockchain sind eine Sammlung von TVM-Zellen oder einfach Zellen, 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 Zellen wird zum Parsen verwendet, weiter wird es klar. Wichtig für uns ist, dass wir an einen 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 Vertragscode 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 eingesehen 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>cEr kann lokal ausgeführt werden, dafür bereiten wir die Umgebung vor.
Wir bemerken, dass in der ersten Zeile Asm.fif, das ist der Code, der in Fift für den Fift-Assembler geschrieben wurde.
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 dort hinein, wobei wir die letzte Zeile ändern, die den Code des Smart Contracts in eine Konstante schreibt. code, um ihn später an die virtuelle Maschine zu übergeben:
"TonUtil.fif" einfügen
"Asm.fif" einfügen
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
recv_internal PROC:
recv_external PROC:
}END> s konstanter Code
Das scheint erstmal klar zu sein, 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 Betrag
0, dictnew, \/\/ contract_address, global_config
1 tuple \/\/ in ein weiteres Tuple einwickeln
konstant c7
0 konstant recv_internal \/\/ um recv_internal() auszuführen
-1 konstant recv_external \/\/ um recv_external() aufzurufenIn c7 Wir zeichnen den Kontext auf, also die Daten, mit denen TVM gestartet wird (oder der Zustand des Netzwerks). Bereits während des Wettbewerbs hatte einer der Entwickler gezeigt, wie es erstellt wird. c7 und ich habe kopiert. In diesem Artikel müssen wir möglicherweise ändern rand_seed , da die Generierung einer Zufallszahl davon abhängt, und wenn wir es nicht ändern, wird immer die gleiche Zahl zurückgegeben.
recv_internal und recv_external Die Konstanten mit dem Wert 0 und -1 stehen für den Aufruf der entsprechenden Funktionen im Smart Contract.
Jetzt sind wir bereit, den ersten Test für unseren leeren Smart Contract zu erstellen. Zur Veranschaulichung werden wir vorerst alle Tests in diese Datei einfügen. lottery-test-suite.fif.
Lassen Sie uns eine Variable erstellen storage und sie leer initialisieren Zellen, dies wird das Speicherhaus des Smart Contracts sein.
message Das ist die Nachricht, die wir dem Smart Contract von außen übergeben werden. Auch diese lassen wir vorerst leer.
Variablenspeicher
<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 erhalten wir Zwischencode auf Fift.
Jetzt können wir den erhaltenen Code ausführen.
export FIFTPATH=~/TON/ton/crypto/fift/lib // einmal zur Vereinfachung ausführen
~/TON/build/crypto/fift -s lottery-test-suite.fif Das Programm sollte fehlerfrei laufen und wir sehen die Ausführung in der Ausgabe:
execute SETCP 0
execute DICTPUSHCONST 19 (xC_,1)
execute DICTIGETJMPZ
execute DROP
execute implicit RET
[ 3][t 0][1582281699.325381279][vm.cpp:479] steps: 5 gas: used=304, max=9223372036854775807, limit=9223372036854775807, credit=0Ausgezeichnet, wir haben die erste funktionierende Version des Smart Contracts geschrieben.
Jetzt müssen wir die Funktionalität erweitern. Zuerst kümmern wir uns um die Nachrichten, die aus der Außenwelt in recv_external()
Der Entwickler wählt selbst das Nachrichtenformat, das der Vertrag akzeptieren kann.
Aber gewöhnlich,
- Erstens möchten wir unseren Vertrag vor der Außenwelt schützen und sicherstellen, dass nur der Vertragseigentümer externe Nachrichten senden kann.
- Zweitens wollen wir, dass eine gültige Nachricht an TON genau einmal gesendet wird und, dass der Smart Contract sie bei einer Wiederholung der gleichen Nachricht ablehnt.
Deshalb werden in fast jedem Vertrag diese beiden Probleme angegangen, da unser Vertrag externe Nachrichten annimmt, müssen wir uns auch darum kümmern.
Lassen Sie uns das in umgekehrter Reihenfolge angehen. Zuerst lösen wir das Problem der Wiederholung: Wenn der Vertrag bereits eine solche Nachricht empfangen und verarbeitet hat, wird er sie nicht ein zweites Mal ausführen. Und dann kümmern wir uns darum, dass nur ein bestimmter Personenkreis Nachrichten an den Smart Contract senden kann.
Es gibt verschiedene Möglichkeiten, um das Problem mit wiederholten Nachrichten zu lösen. So gehen wir vor. Im Smart Contract initialisieren wir einen Zähler für empfangene Nachrichten mit dem Startwert 0. Bei jeder Nachricht fügen wir dem Smart Contract den aktuellen Wert des Zählers hinzu. Wenn der Zählerwert in der Nachricht nicht mit dem 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.
Zurück zu lottery-test-suite.fif und fügen ihm den zweiten Test hinzu. Wir senden eine falsche Nummer, der Code sollte eine Ausnahme auslösen. Angenommen, im Vertrag sind 166 gespeichert, wir senden jedoch 165.
<b 166 32 u, b> Speicher !
<b 165 32 u, b> Nachricht !
Nachricht @
recv_external
Code
Speicher @
c7
runvmctx
drop
exit_code !
."Ausgangs-Code " exit_code @ . cr
exit_code @ 33 - abort"Test #2 nicht bestanden"Wir 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 bestandenIn diesem Stadium lottery-test-suite.fif sollte aussehen wie .
Jetzt lassen Sie uns die Logik des Zählers im Smart Contract hinzufügen. 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);
}In slice in_msg liegt die Nachricht, die wir senden.
Das Erste, was wir tun, ist zu überprüfen, ob im Nachrichtendaten vorhanden sind. Wenn nicht, verlassen wir einfach das Verfahren.
Dann parsen wir die Nachricht. in_msg~load_uint(32) lädt die Zahl 165, 32-Bit unsigned int aus der übermittelten Nachricht.
Dann laden wir 32 Bit aus dem Speicher des Smart Contracts und überprüfen, ob die geladene Zahl mit der übermittelten übereinstimmt. Wenn nicht, wird eine Ausnahme ausgelöst. In unserem Fall soll eine Ausnahme ausgelöst werden, da wir eine nicht übereinstimmende Zahl übermitteln.
Jetzt kompilieren wir.
~~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc Den resultierenden Code kopieren wir in lottery-test-suite.fif, dabei vergessen wir nicht, die letzte Zeile zu ersetzen.
Wir überprüfen, ob der Test bestanden wird:
~/TON/build/crypto/fift -s lottery-test-suite.fifder entsprechende Commit mit den aktuellen Ergebnissen angesehen werden.
Wir stellen fest, dass es unpraktisch ist, den kompilierten Code des Smart Contracts ständig in die Testdatei zu kopieren, daher schreiben wir ein Skript, das den Code für uns in eine Konstante schreibt, und wir binden einfach den kompilierten Code in unsere Tests mit Hilfe von "include".
Im Projektverzeichnis 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 sie ausführbar.
chmod +x ./build.shJetzt müssen wir nur noch unser Skript ausführen, um den Vertrag zu kompilieren. Darüber hinaus müssen wir ihn in eine Konstante schreiben code. Daher erstellen wir eine neue Datei lotter-compiled-for-test.fif, die wir in die Datei einfügen werden lottery-test-suite.fif.
Fügen wir dem sh-Skript Code hinzu, der die kompilierte Datei einfach in lotter-compiled-for-test.fif kopiert und die letzte Zeile ä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 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
In lottery-test-suite.fif löschen den Vertragscode und fügen die Zeile hinzu "lottery-compiled-for-test.fif" include.
Wir führen die Tests aus, um zu überprüfen, ob sie bestehen.
~/TON/build/crypto/fift -s lottery-test-suite.fifSuper, jetzt erstellen wir eine Datei, um den Teststart zu automatisieren test.sh, die zuerst build.sh, und dann die Tests ausführt.
touch test.sh
chmod +x test.shWir schreiben hinein
./build.sh
echo "nKompilation abgeschlossenn"
export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fifWir machen test.sh und führen es aus, um die Funktionsfähigkeit der Tests zu überprüfen.
chmod +x ./test.sh
./test.shWir überprüfen, ob der Vertrag kompiliert und die Tests durchgeführt werden.
Super, jetzt bei der Ausführung test.sh Die Kompilierung und der Testlauf werden sofort durchgeführt. Hier ist der Link zu .
Okay, bevor wir weitermachen, lasst uns noch eine Sache für den Komfort erledigen.
Lass uns einen Ordner erstellen build in dem wir den kompilierten Vertrag und seine Klone in einer Konstante speichern werden. lottery-compiled.fif, lottery-compiled-for-test.fif. Außerdem erstellen wir einen Ordner test in dem die Testdatei gespeichert wird lottery-test-suite.fif und potenziell andere Hilfsdateien. .
Fahren wir mit der Entwicklung des Smart Contracts fort.
Als nächstes sollte es einen Test geben, der überprüft, ob die 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` ist eine 32-Bit positive ganze Zahl, die als Zähler dient.
`pubkey` ist eine 256-Bit positive ganze Zahl, der öffentliche Schlüssel, mit dem wir die Signatur einer extern gesendeten Nachricht überprüfen werden, wie unten beschrieben.
`order_seqno` ist eine 32-Bit positive ganze Zahl, die den Zähler der Anzahl der Einsätze speichert.
`number_of_wins` ist eine 32-Bit positive ganze Zahl, die die Anzahl der Siege speichert.
`incoming_amount` ist ein Datentyp Gram (die ersten 4 Bits geben die Länge an), der die gesamte Menge an Gramm speichert, die an den Smart Contract gesendet wurde.
`outgoing_amount` ist die Gesamtsumme an Gramm, die an die Gewinner ausgezahlt wurde.
`owner_wc` ist die Nummer des Workchains, eine 32-Bit (an einigen Stellen auch als 8-Bit bezeichnet) ganze Zahl. Momentan gibt es nur zwei -1 und 0.
`owner_account_id` ist eine 256-Bit positive ganze Zahl, die die Adresse des Vertrags im aktuellen Workchain speichert.
`orders` ist eine Dictionary-Variable, die die letzten zwanzig Einsätze speichert. Als nächstes müssen zwei Funktionen geschrieben werden. Die erste nennen wir pack_state(), die die Daten für die spätere Speicherung im Smart Contract speichert. Die zweite, die wir unpack_state() nennen, 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;
}Wir fügen diese beiden Funktionen am Anfang des Smart Contracts hinzu. Es ergibt sich ein Zwischenergebnis.
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, wo wir nützliche Funktionen zum Schreiben und Lesen von Daten haben, können wir weiter vorankommen.
Wir müssen überprüfen, ob die eingehende Nachricht von dem Vertragsinhaber (oder einem anderen Benutzer, der Zugang zum privaten Schlüssel hat) signiert wurde.
Wenn wir einen Smart Contract veröffentlichen, 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 aufzeichnen, 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 notieren ihn in test/keys/owner.pk. Dazu starten wir Fift im interaktiven Modus und führen vier Befehle aus.
`newkeypair` generiert ein Paar aus öffentlichem und privatem Schlüssel und schreibt sie in den Stack.
`drop` entfernt das oberste Element (in diesem Fall den öffentlichen Schlüssel) aus dem Stack.
`.s` zeigt an, was sich derzeit im Stack befindet.
`"owner.pk" B>file` speichert den privaten Schlüssel in eine Datei mit dem Namen `owner.pk`.
`bye` beendet die Arbeit mit Fift. Lass uns einen Ordner erstellen keys im Ordner 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 die Datei owner.pk.
Wir entfernen den öffentlichen Schlüssel aus dem Stack; wenn wir ihn benötigen, können wir ihn aus dem privaten erhalten.
Jetzt müssen wir die Signaturprüfung schreiben. Lassen Sie uns mit dem Test beginnen. Zuerst lesen wir den privaten Schlüssel aus einer Datei mit 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 einen ö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 !Beide Schlüssel werden benötigt.
Wir initialisieren den Smart-Contract-Speicher mit zufälligen Daten in der gleichen Reihenfolge wie in der Funktion pack_state()und speichern in der Variablen storage.
variable besitzer_privater_schluessel
variable besitzer_oeffentlicher_schluessel
variable bestellungen
variable besitzer_wc
variable besitzer_kontonummer
"./keys/besitzer.pk" Datei>B besitzer_privater_schluessel !
besitzer_privater_schluessel @ priv>pub besitzer_oeffentlicher_schluessel !
dictnew bestellungen !
0 besitzer_wc !
0 besitzer_kontonummer !
<b 0 32 u, owner_public_key @ b, gram, owner_wc i, owner_account_id 256 orders dict, b> Speicher !Als nächstes erstellen wir die signierte Nachricht, die nur die Signatur und den Zählerwert enthalten wird.
Zuerst erstellen wir die Daten, die wir übertragen 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 eigentuemer_privater_schluessel @ ed25519_sign_uint signatur !
<b signature @ b, 0 32 u, b> <s ! Das Ergebnis, die Nachricht, die wir an den Smart Contract senden werden, ist in der Variablen message_to_send, bezüglich der Funktionen hashu, ed25519_sign_uint finden Sie hier. .
Und um den Test erneut zu starten, rufen wir wieder auf.
message_to_send @
recv_external
code
storage @
c7
runvmctxDie Datei mit den Tests sollte in diesem Schritt so aussehen.
Wir werden den Test starten und er wird fehlschlagen. Daher müssen wir den Smart Contract ändern, damit er Nachrichten in diesem Format empfangen und die Signatur überprüfen kann.
Zuerst lesen wir 512 Bits der Signatur aus der Nachricht aus und speichern sie in einer Variablen. Danach lesen wir 32 Bits der Zähler-Variablen.
Da wir eine Funktion zum Lesen von Daten aus dem Smart Contract-Speicher haben, werden wir diese verwenden.
Als Nächstes erfolgt die Überprüfung des Zählers, der mit dem Speicher übergeben wurde, und die Überprüfung der 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: Mangel an Bits in der Nachricht und Mangel an Bits im Speicher, weshalb der Code beim Parsen fehlschlägt. Wir müssen die Signatur der Nachricht, die wir senden, hinzufügen 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. Die Datei mit den Tests sieht im Moment so aus.
Lass uns den vierten Test schreiben, in dem wir eine Nachricht mit einem fremden privaten Schlüssel signieren werden. Wir erstellen einen weiteren privaten Schlüssel und speichern ihn in einer Datei. not-owner.pk. Mit diesem privaten Schlüssel signieren wir die Nachricht. Lass uns die Tests ausführen und sicherstellen, dass alle Tests bestehen. zum aktuellen Zeitpunkt.
Nun können wir endlich mit der Implementierung der Logik des Smart Contracts beginnen.
In 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 bei der Erstellung des Vertrags im Speicher festgehalten.
Für den Fall der Fälle benötigen wir die Möglichkeit, die Adresse, an die die verlorenen Gramm überwiesen werden, zu ändern. Zudem sollten wir in der Lage sein, Gramm von der Lotterie an die Adresse des Besitzers zu senden.
Lass uns mit dem ersten beginnen. Wir schreiben zuerst einen Test, der überprüft, dass nach dem Senden der Nachricht der Smart Contract die neue Adresse im Speicher gespeichert hat. Beachte, dass wir neben dem Zähler und der neuen Adresse auch Aktion Eine 7-Bit-Ganzzahl, die von uns gewählt wird, bestimmt, wie die Nachricht im Smart Contract verarbeitet wird.
<b 0 32 u, 1 @ 7 new_owner_wc i, new_owner_account_id 256 b> Nachricht zur Unterzeichnung !Im Test kann man sehen, wie die Deserialisierung des Smart Contract-Speichers erfolgt. storage in Fift. Die Deserialisierung von Variablen ist in der Fift-Dokumentation beschrieben.
mit dem hinzugefügten Test.
Wir führen den Test aus und stellen sicher, dass er fehlschlägt. Jetzt fügen wir die Logik zur Änderung der Besitzeradresse der Lotterie hinzu.
Im Smart Contract analysieren wir weiter message, lesen ein Aktion. Wir erinnern uns, dass wir zwei haben werden Aktion: Änderung der Adresse und Versendung von Grams.
Dann lesen wir die neue Adresse des Vertragsinhabers 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 analysiert, die im Test fehlen. Fügen wir in die Nachricht einen nicht existierenden Aktion. Wir führen die Tests aus und sehen, dass alle bestehen. Commit zu den Änderungen. Ausgezeichnet.
Jetzt schreiben wir die Logik, um die angegebene Menge an Grams an die zuvor gespeicherte Adresse zu senden.
Zuerst schreiben wir einen Test. Wir werden zwei Tests schreiben: einen, wenn das Guthaben nicht ausreicht, und einen, wenn alles erfolgreich sein sollte. Die Tests können angesehen werden .
Jetzt werden wir den Code fertigstellen. Zuerst schreiben wir zwei Hilfsmethoden. Die erste ist eine Getter-Methode, um den aktuellen Kontostand des Smart Contracts zu ermitteln.
int balance() inline_ref method_id {
return get_balance().pair_first();
}Und die zweite zum Versenden 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 <= format indicator int_msg_info$0
;; .store_uint(1, 1) ;; 1 <= ihr disabled
;; .store_uint(1, 1) ;; 1 <= bounce = true
;; .store_uint(0, 1) ;; 0 <= bounced = false
;; .store_uint(4, 5) ;; 00100 <= address flags, anycast = false, 8-bit workchain
.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 Gebühren, die vom Absender bezahlt werden, 64 zum Zurückgeben des Wertes der eingehenden Nachricht
}Wir fügen diese beiden Methoden in den Smart Contract ein und schreiben die Logik. Zuerst parsen wir die Menge an Gramm aus der Nachricht. Dann überprüfen wir den Kontostand; wenn nicht genug vorhanden ist, werfen wir eine Ausnahme aus. Wenn alles gut ist, senden wir die Gramm 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 behandelte Nachricht eine Gebühr vom Smart Contract abgezogen. Damit der Smart Contract die Anfrage erfüllt, müssen nach den grundlegenden Prüfungen accept_message().
Jetzt kümmern wir uns um die internen Nachrichten. Tatsächlich werden wir nur Gramm entgegennehmen und dem Spieler im Gewinnfall den doppelten Betrag und im Verlustfall ein Drittel an den Eigentümer zurücksenden.
Zuerst schreiben wir einen einfachen Test. Dazu benötigen wir die Testadresse des Smart Contracts, von der wir angeblich 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-positiven einzigartigen Kontonummer in dieser Workchain. Zum Beispiel: -1 und 12345, diese Adresse werden wir in einer Datei speichern.
Ich habe die Funktion zum Speichern der Adresse kopiert 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 betrachten, wie die Funktion funktioniert, um zu verstehen, wie Fift arbeitet. 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" Der nächste Schritt ist, die Funktion auszuführen -rot, die den Stack so verschiebt, dass die einzigartige Nummer des Smart Contracts oben auf dem Stack liegt:
"sender.addr" -1 12345256 u>B konvertiert die 256-Bit-positive 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 Bytefolgen.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFFWieder Swap.
BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" Und schließlich werden die Bytes in die Datei geschrieben B>datei. Danach ist unser Stack leer. Wir stoppen Fift. In dem aktuellen Ordner wurde eine Datei erstellt sender.addr. Wir verschieben die Datei in den erstellten Ordner test/adressen/.
Wir schreiben einen einfachen Test, der Grams an den Smart Contract sendet. .
Jetzt kümmern wir uns um die Logik der Lotterie.
Das erste, was wir tun, ist, die Nachricht zu überprüfen bounced oder nicht, wenn bounced, dann ignorieren wir. bounced Das bedeutet, dass der Vertrag Grams zurückgeben wird, wenn ein Fehler auftritt. Wir werden die Grams nicht zurückgeben, falls ein Fehler auftritt.
Wir überprüfen das Balance, wenn es weniger als ein halbes Gramm beträgt, akzeptieren wir einfach die Nachricht und ignorieren sie.
Dann parsen wir die Adresse des Smart Contracts, von dem die Nachricht kam.
Wir lesen die Daten aus dem Speicher und dann löschen wir 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().
Dann schauen wir, wenn das Guthaben für die Auszahlung nicht ausreicht, betrachten wir es nicht als Wette, sondern als Einzahlung und speichern die Einzahlung in orders.
Und schließlich kommen wir zum Kern des Smart Contracts.
Zuerst, wenn der Spieler verliert, 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.
Gewinnt der Spieler jedoch, senden wir den doppelten Betrag an die Adresse des Spielers und speichern die Informationen über die 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 das einfache, wir werden Getter-Methoden erstellen, um Informationen über den Status des Vertrags aus der Außenwelt abzurufen (faktisch die Daten aus dem Speicher des Smartcontracts abzulesen).
. Wie man Informationen über den Smartcontract erhält, werden wir weiter unten schreiben.
Außerdem habe ich vergessen, den Code hinzuzufügen, der die erste Anfrage verarbeiten wird, die beim Veröffentlichen des Smartcontracts erfolgt. . Und außerdem den Bug mit der Überweisung von 1/3 des Betrags auf das Konto des Eigentümers.
Als nächstes bleibt es, den Smartcontract zu veröffentlichen. Erstellen wir einen Ordner requests.
Ich habe den Code zur Veröffentlichung genommen der im offiziellen Repository.
Es gibt einige Punkte, auf die man achten sollte. Wir erstellen den Speicher des Smartcontracts und die Eingangsmessage. Danach wird die Adresse des Smartcontracts generiert, d.h. die Adresse ist bereits vor der Veröffentlichung in TON bekannt. Dann muss man einige Gramm an diese Adresse senden, und erst danach muss die Datei mit dem Smartcontract selbst gesendet werden, da die Netzwerke Gebühren für die Speicherung des Smartcontracts und die darin durchgeführten Operationen erheben (Validatoren, die Smartcontracts speichern und ausführen). .
Jetzt 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ührungsprotokollen die Adresse des Smart Contracts.
neue Wallet-Adresse = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a
(Speichere Adresse in der Datei lottery.addr)
Nicht rückgebbare Adresse (für die Initialisierung): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Rückgebbare Adresse (für späteren Zugriff): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFYWie interessant, 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.
Konto ist leerSenden an die Adresse 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Grams und nach ein paar Sekunden führen wir denselben Befehl erneut aus. Zum Senden von Grams verwende ich , und Testgrams können von jemandem aus dem Chat angefordert werden, über den ich am Ende des Artikels sprechen werde.
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydEs wird gezeigt, dass ein nicht initialisierter (state:account_uninit) Smart Contract mit dieser Adresse und einem Balance von 1 000 000 000 Nanogramm im Netzwerk erschienen ist.
Der Kontostand ist (Konto
Adresse:(addr_std
anycast:nothing workchain_id:0 Adresse:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
speicher_stat:(storage_info
verwendet:(storage_used
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:(account_storage last_trans_lt:3825478000002
gesamtbetrag:(währungen
gramm:(nanograms
betrag:(var_uint len:4 value:2000000000))
andere:(extra_währungen
dict:hme_empty))
zustand:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
Letzte Transaktion lt = 3825478000001 Hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
Der Kontostand beträgt 2000000000ngJetzt veröffentlichen wir den Smart Contract. Lassen Sie uns den lite-client starten und ausführen.
> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] Anfrage aus der Datei lottery-query.boc senden
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query] Externer Nachrichtenstatus ist 1 Überprüfen wir, ob der Vertrag veröffentlicht wurde.
> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydUnter anderem erhalten wir.
speicher:(account_storage last_trans_lt:3825499000002
gesamtbetrag:(währungen
gramm:(nanograms
betrag:(var_uint len:4 value:1987150999))
andere:(extra_währungen
dict:hme_empty))
zustand:(account_activeWir sehen, dass account_active.
Der entsprechende Commit mit Änderungen .
Jetzt erstellen wir Anfragen zur Interaktion mit dem Smart Contract.
Genauer gesagt, die erste Änderung der Adresse lassen wir als eigenständige Aufgabe, während wir die zweite für die Übertragung der Gramm auf die Adresse des Eigentümers übernehmen. Tatsächlich müssen wir dasselbe tun wie im Test zur Übertragung der Gramm.
Diese Nachricht werden wir an den Smart Contract senden, wo msg_seqno 165, Aktion 2 und 9,5 Gramm zur Übertragung.
<b 165 32 u, 2 7 9500000000 gram, b>Vergessen wir nicht, 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 mithilfe von Get-Methoden
Jetzt schauen wir uns an, wie man die Get-Methoden des Smart Contracts ausführt.
Starten wir lite-client und führen die Get-Methoden aus, die wir geschrieben haben.
$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
arguments: [ 104128 ]
result: [ 64633878952 ]
...In result enthält den Wert, den die Funktion balance() aus unserem Smart Contract zurückgibt.
Dasselbe führen wir auch für mehrere Methoden aus.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments: [ 77871 ]
result: [ 1 ] Wir werden die Historie der Wetten anfordern.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
arguments: [ 67442 ]
result: [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] Wir werden den 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 erstellt, um die Daten des Smart Contracts in einer benutzerfreundlichen Weise anzuzeigen. Ich gehe hier nicht detailliert darauf ein und werde die Website veröffentlichen. .
Die Anfragen an TON werden von Python mit Hilfe von lite-client. Zur Vereinfachung wird die Website in Docker gepackt und auf Google Cloud veröffentlicht. .
Lass es uns versuchen
Jetzt versuchen wir, Gramm für das Aufladen von . Wir senden 40 Gramm und 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 er auch kürzer sein können, aber vielleicht ist er gerade richtig für jemanden, der nichts über TON weiß und einen nicht ganz einfachen Smart Contract entwerfen und veröffentlichen möchte. Einige Dinge hätte man vielleicht einfacher erklären können.
Es ist möglich, dass einige Aspekte der Implementierung effizienter und eleganter hätten gelöst werden können, aber dann hätte noch mehr Zeit für die Vorbereitung des Artikels benötigt. Es könnte auch sein, dass ich an irgendeiner Stelle einen Fehler gemacht habe oder etwas nicht verstanden habe. Daher sollten Sie sich, wenn Sie etwas Ernsthaftes unternehmen, auf die offizielle Dokumentation oder das offizielle Repository mit dem TON-Code stützen.
Es sei darauf hingewiesen, dass TON sich noch in der aktiven Entwicklungsphase befindet und Änderungen auftreten können, die einen der Schritte in diesem Artikel brechen (was bereits während des Schreibens geschehen ist, ich habe es jedoch bereits korrigiert). Der allgemeine Ansatz wird sich jedoch wahrscheinlich nicht ändern.
Über die Zukunft von TON möchte ich nicht spekulieren. Möglicherweise wird die Plattform etwas Größeres werden, und wir sollten jetzt Zeit investieren, um sie zu studieren und unsere Nische mit eigenen Produkten bereits jetzt zu besetzen.
Es gibt auch Libra von Facebook, dessen potenzielle Nutzerbasis größer ist als die von TON. Über Libra weiß ich fast nichts, aber laut dem Forum ist dort die Aktivität viel höher als in der TON-Community. Obwohl die Entwickler und die TON-Community eher wie ein Underground erscheinen, ist das auch cool.
Links
- Offizielle Dokumentation zu TON:
- Offizielles Repository von TON:
- Offizielles Wallet für verschiedene Plattformen:
- Repository des Smart Contracts aus diesem Artikel:
- Link zur Smart Contract-Website:
- Repository für die Visual Studio Code-Erweiterung für FunC:
- Telegram-Chat über TON, der mir in der Anfangsphase sehr geholfen hat. Ich denke, es wäre nicht falsch zu sagen, dass dort alle sind, die etwas für TON geschrieben haben. Dort kann man auch um Testguthaben bitten.
- Ein weiterer Chat über TON, in dem ich nützliche Informationen gefunden habe:
- Erste Phase des Wettbewerbs:
- Zweite Phase des Wettbewerbs:
Quelle: habr.com
