Informationen zum Schreiben und Veröffentlichen eines Smart Contracts im Telegram Open Network (TON)

Informationen zum Schreiben und Veröffentlichen eines Smart Contracts in TON

Worum geht es in diesem Artikel?

In dem Artikel werde ich darüber sprechen, wie ich am ersten (von zwei) Telegram-Blockchain-Wettbewerb teilgenommen habe, keinen Preis gewonnen habe und beschlossen habe, meine Erfahrungen in einem Artikel festzuhalten, damit sie nicht in Vergessenheit geraten und vielleicht helfen jemand.

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 und eine Website geschrieben, die Smart Contract-Daten direkt von TON anzeigt, ohne Zwischenspeicher zu verwenden.

Der Artikel wird für diejenigen nützlich sein, die ihren ersten Smart Contract in TON abschließen möchten, aber nicht wissen, wo sie anfangen sollen.

Am Beispiel der Lotterie werde ich von der Installation der Umgebung über die Veröffentlichung eines Smart Contracts, die Interaktion mit ihm und das Schreiben einer Website zum Empfangen und Veröffentlichen von Daten gehen.

Über die Teilnahme am Wettbewerb

Im vergangenen Oktober kündigte Telegram einen Blockchain-Wettbewerb mit neuen Sprachen an Fift и FunC. Es war notwendig, einen der fünf vorgeschlagenen Smart Contracts zu schreiben. Ich dachte, es wäre schön, etwas anderes zu machen, eine Sprache zu lernen und etwas zu machen, auch wenn ich in Zukunft nichts anderes schreiben muss. Außerdem ist das Thema ständig in aller Munde.

Es ist erwähnenswert, dass ich keine Erfahrung mit der Entwicklung intelligenter Verträge hatte.

Ich hatte vor, bis zum Schluss mitzumachen, bis ich konnte, und dann einen Rezensionsartikel zu schreiben, aber beim ersten bin ich gleich gescheitert. ICH hat eine Brieftasche geschrieben mit aktivierter Mehrfachsignatur FunC und es hat im Allgemeinen funktioniert. Ich habe es als Grundlage genommen Smart Contract auf Solidity.

Damals dachte ich, dass das auf jeden Fall ausreichen würde, um zumindest einen Preisplatz zu ergattern. Dadurch wurden etwa 40 von 60 Teilnehmern Preisträger und ich war nicht darunter. Im Allgemeinen ist daran nichts auszusetzen, aber eines hat mich gestört. Zum Zeitpunkt der Bekanntgabe der Ergebnisse war die Überprüfung des Tests für meinen Vertrag noch nicht abgeschlossen, ich fragte die Teilnehmer im Chat, ob es noch jemanden gäbe, der ihn nicht hatte, es gab keinen.

Anscheinend achteten die Juroren auf meine Nachrichten, veröffentlichten aber zwei Tage später einen Kommentar und ich verstehe immer noch nicht, ob sie meinen Smart-Vertrag während der Beurteilung versehentlich übersehen haben oder einfach dachten, er sei so schlecht, dass er keines Kommentars bedürfe. Ich habe auf der Seite eine Frage gestellt, aber keine Antwort erhalten. Obwohl es kein Geheimnis ist, wer urteilte, hielt ich es für unnötig, persönliche Nachrichten zu schreiben.

Es wurde viel Zeit darauf verwendet, es zu verstehen, daher wurde beschlossen, einen Artikel zu schreiben. Da es noch nicht viele Informationen gibt, hilft dieser Artikel allen Interessierten, Zeit zu sparen.

Das Konzept der Smart Contracts in TON

Bevor Sie etwas schreiben, müssen Sie herausfinden, von welcher Seite Sie an diese Sache herangehen. Deshalb erzähle ich Ihnen jetzt, aus welchen Teilen das System besteht. Genauer gesagt, welche Teile Sie wissen müssen, um zumindest eine Art Arbeitsvertrag zu schreiben.

Wir werden uns darauf konzentrieren, einen intelligenten Vertrag zu schreiben und damit zu arbeiten TON Virtual Machine (TVM), Fift и FunCDaher ähnelt der Artikel eher einer Beschreibung der Entwicklung eines regulären Programms. Wir werden hier nicht näher darauf eingehen, wie die Plattform selbst funktioniert.

Im Allgemeinen darüber, wie es funktioniert TVM und Sprache Fift Es gibt eine gute offizielle Dokumentation. Während der Teilnahme am Wettbewerb und jetzt beim Schreiben des aktuellen Vertrags habe ich mich oft an sie gewandt.

Die Hauptsprache, in der Smart Contracts geschrieben werden, ist FunC. Derzeit gibt es keine Dokumentation dazu. Um also etwas zu schreiben, müssen Sie Beispiele für Smart Contracts aus dem offiziellen Repository und die Implementierung der Sprache selbst dort studieren. Außerdem können Sie sich Beispiele für Smart Contracts aus den letzten beiden ansehen Wettbewerbe. Links am Ende des Artikels.

Nehmen wir an, wir haben bereits einen Smart Contract für geschrieben FunCAnschließend kompilieren wir den Code in den Fift-Assembler.

Der zusammengestellte Smart Contract muss noch veröffentlicht werden. Dazu müssen Sie eine Funktion schreiben Fift, das den Smart-Contract-Code und einige andere Parameter als Eingabe verwendet und die Ausgabe eine Datei mit der Erweiterung ist .boc (was „Beutel mit Zellen“ bedeutet) und, je nachdem, wie wir es schreiben, einen privaten Schlüssel und eine Adresse, die auf Basis des Smart-Contract-Codes generiert werden. Sie können Gramm bereits an die Adresse eines Smart Contracts senden, der noch nicht veröffentlicht wurde.

Um einen Smart-Vertrag in TON zu veröffentlichen, erhalten Sie .boc Die Datei muss mithilfe eines Light-Clients an die Blockchain gesendet werden (mehr dazu weiter unten). Vor der Veröffentlichung müssen Sie jedoch Gramm an die generierte Adresse übertragen, andernfalls wird der Smart Contract nicht veröffentlicht. Nach der Veröffentlichung können Sie mit dem Smart-Vertrag interagieren, indem Sie ihm Nachrichten von außen (z. B. mithilfe eines Light-Clients) oder von innen (z. B. sendet ein Smart-Vertrag einem anderen eine Nachricht innerhalb von TON) senden.

Sobald wir verstehen, wie der Code veröffentlicht wird, wird es einfacher. Wir wissen ungefähr, was wir schreiben wollen und wie unser Programm funktionieren wird. Und während wir schreiben, achten wir darauf, wie dies bereits in bestehenden Smart Contracts implementiert ist, oder schauen uns den Implementierungscode an Fift и FunC im offiziellen Repository oder schauen Sie in der offiziellen Dokumentation nach.

Sehr oft habe ich im Telegram-Chat, in dem sich alle Wettbewerbsteilnehmer und Telegram-Mitarbeiter versammelten, nach Schlüsselwörtern gesucht, und so kam es, dass sich während des Wettbewerbs alle dort versammelten und anfingen, über Fift und FunC zu diskutieren. Link am Ende des Artikels.

Es ist Zeit, von der Theorie zur Praxis überzugehen.

Vorbereiten der Umgebung für die Arbeit mit TON

Ich habe alles, was im Artikel beschrieben wird, auf MacOS gemacht und es in sauberem Ubuntu 18.04 LTS auf Docker noch einmal überprüft.

Als Erstes müssen Sie es herunterladen und installieren lite-client mit dem Sie Anfragen an TON senden können.

Die Anweisungen auf der offiziellen Website beschreiben den Installationsprozess sehr detailliert und klar und lassen einige Details aus. Hier folgen wir den Anweisungen und installieren nebenbei die fehlenden Abhängigkeiten. Ich habe nicht jedes Projekt selbst kompiliert und aus dem offiziellen Ubuntu-Repository installiert (auf MacOS, das ich verwendet habe). 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 

Sobald alle Abhängigkeiten installiert sind, können Sie mit der Installation beginnen lite-client, Fift, FunC.

Zuerst klonen wir das TON-Repository zusammen mit seinen Abhängigkeiten. Der Einfachheit halber erledigen wir alles in einem Ordner ~/TON.

cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursive

Das Repository speichert auch Implementierungen Fift и FunC.

Jetzt sind wir bereit, das Projekt zusammenzustellen. Der Repository-Code wird in einen Ordner geklont ~/TON/ton. In ~/TON Erstellen Sie einen Ordner build und sammle das Projekt darin.

mkdir ~/TON/build 
cd ~/TON/build
cmake ../ton

Da wir einen intelligenten Vertrag schreiben werden, brauchen wir nicht nur lite-clientAber Fift с FunC, also lasst uns alles kompilieren. Es ist kein schneller Prozess, also warten wir.

cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target func

Laden Sie als Nächstes die Konfigurationsdatei herunter, die Daten über den Knoten enthält, zu dem lite-client wird verbinden.

wget https://test.ton.org/ton-lite-client-test1.config.json

Die ersten Anfragen an TON stellen

Jetzt lasst uns starten lite-client.

cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.json

Wenn der Build erfolgreich war, sehen Sie nach dem Start ein Protokoll der Verbindung des Light-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)
...

Sie können den Befehl ausführen help und sehen Sie, welche Befehle verfügbar sind.

help

Lassen Sie uns die Befehle auflisten, die wir in diesem Artikel verwenden werden.

list of available commands:
last    Get last block and state info from server
sendfile <filename> Load a serialized message from <filename> and send it to server
getaccount <addr> [<block-id-ext>]  Loads the most recent state of specified account; <addr> is in [<workchain>:]<hex-or-base64-addr> format
runmethod <addr> [<block-id-ext>] <method-id> <params>...   Runs GET method <method-id> of account <addr> with specified parameters

last получает последний созданный блок с сервера. 

sendfile <filename> отправляет в TON файл с сообщением, именно с помощью этой команды публикуется смарт-контракт и запрсосы к нему. 

getaccount <addr> загружает текущее состояние смарт-контракта с указанным адресом. 

runmethod <addr> [<block-id-ext>] <method-id> <params>  запускает get-методы смартконтракта. 

Jetzt sind wir bereit, den Vertrag selbst zu schreiben.

Implementierung

Idee

Wie ich oben geschrieben habe, ist der Smart Contract, den wir schreiben, eine Lotterie.

Darüber hinaus handelt es sich hierbei nicht um eine Lotterie, bei der Sie ein Ticket kaufen und eine Stunde, einen Tag oder einen Monat warten müssen, sondern um eine Sofortlotterie, bei der der Benutzer zur Vertragsadresse wechselt N Gramm, und bekommt es sofort zurück 2 * N Gramm oder verliert. Wir werden die Gewinnwahrscheinlichkeit auf etwa 40 % erhöhen. Sollten nicht genügend Gramm zur Zahlung vorhanden sein, betrachten wir die Transaktion als Aufladung.

Darüber hinaus ist es wichtig, dass Wetten in Echtzeit und in einer komfortablen Form eingesehen werden können, sodass der Benutzer sofort erkennen kann, ob er gewonnen oder verloren hat. Daher müssen Sie eine Website erstellen, auf der Wetten und Ergebnisse direkt von TON angezeigt werden.

Einen Smart Contract schreiben

Der Einfachheit halber habe ich den Code für FunC hervorgehoben; das Plugin kann in der Visual Studio-Codesuche gefunden und installiert werden; wenn Sie plötzlich etwas hinzufügen möchten, habe ich das Plugin öffentlich verfügbar gemacht. Außerdem hat jemand zuvor ein Plugin für die Arbeit mit Fift erstellt. Sie können es auch installieren und in VSC finden.

Lassen Sie uns sofort ein Repository erstellen, in das wir die Zwischenergebnisse übertragen.

Um uns das Leben zu erleichtern, schreiben wir einen Smart Contract und testen ihn lokal, bis er fertig ist. Erst danach werden wir es in TON veröffentlichen.

Der Smart Contract verfügt über zwei externe Methoden, auf die zugegriffen werden kann. Erste, recv_external() Diese Funktion wird ausgeführt, wenn eine Vertragsanfrage von außen kommt, also nicht von TON, beispielsweise wenn wir selbst eine Nachricht generieren und diese über den Lite-Client senden. Zweite, recv_internal() Dies ist der Fall, wenn sich innerhalb von TON jeder Vertrag auf unseren bezieht. In beiden Fällen können Sie Parameter an die Funktion übergeben.

Beginnen wir mit einem einfachen Beispiel, das bei Veröffentlichung funktioniert, aber keine funktionale Belastung enthält.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementation 
}

() recv_external(slice in_msg) impure {
    ;; TODO: implementation  
}

Hier müssen wir erklären, was es ist slice. Alle in der TON-Blockchain gespeicherten Daten sind eine Sammlung TVM cell Röhr "Ryo RїSЂRѕSЃS, Rѕ cellIn einer solchen Zelle können Sie bis zu 1023 Datenbits und bis zu 4 Links zu anderen Zellen speichern.

TVM cell slice oder slice Dies ist Teil des bestehenden cell zum Parsen verwendet wird, wird später klar. Das Wichtigste für uns ist, dass wir transferieren können slice und verarbeiten Sie die Daten je nach Art der Nachricht in recv_external() oder recv_internal().

impure – ein Schlüsselwort, das angibt, dass die Funktion Smart-Contract-Daten ändert.

Speichern wir den Vertragscode in lottery-code.fc und kompilieren.

~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc 

Die Bedeutung der Flags kann mit dem Befehl eingesehen werden

~/TON/build/crypto/func -help

Wir haben den Fift-Assembler-Code kompiliert 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>c

Es kann lokal gestartet werden, dafür bereiten wir die Umgebung vor.

Beachten Sie, dass die erste Zeile eine Verbindung herstellt Asm.fif, das ist in Fift geschriebener 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 Sie den kompilierten Code dorthin und ersetzen Sie dabei die letzte Zeile darin, die den Smart-Contract-Code in eine Konstante schreibt codeum es dann auf die virtuelle Maschine zu übertragen:

"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

Soweit es klar erscheint, fügen wir nun derselben Datei den Code hinzu, den wir zum Starten 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 , , // remaining balance
0 , dictnew , // contract_address, global_config
1 tuple // wrap to another tuple
constant c7

0 constant recv_internal // to run recv_internal() 
-1 constant recv_external // to invoke recv_external()

В c7 Wir zeichnen den Kontext auf, also die Daten, mit denen der TVM (oder Netzwerkstatus) gestartet wird. Schon während des Wettbewerbs zeigte einer der Entwickler, wie man erstellt c7 und ich habe kopiert. In diesem Artikel müssen wir möglicherweise etwas ändern rand_seed da die Generierung einer Zufallszahl davon abhängt und wenn sie nicht geändert wird, wird jedes Mal dieselbe Zahl zurückgegeben.

recv_internal и recv_external Konstanten mit den Werten 0 und -1 sind für den Aufruf der entsprechenden Funktionen im Smart Contract verantwortlich.

Jetzt sind wir bereit, den ersten Test für unseren leeren Smart Contract zu erstellen. Aus Gründen der Übersichtlichkeit fügen wir vorerst alle Tests derselben Datei hinzu lottery-test-suite.fif.

Lassen Sie uns eine Variable erstellen storage und schreibe ein leeres hinein cellDies wird der Smart-Contract-Speicher sein.

message Dies ist die Nachricht, die wir von außen an den intelligenten Kontakt übermitteln. Wir werden es vorerst auch leer machen.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Nachdem wir die Konstanten und Variablen vorbereitet haben, starten wir TVM mit dem Befehl runvmctx und übergeben Sie die erstellten Parameter an die Eingabe.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Am Ende werden wir Erfolg haben so Zwischencode für Fift.

Jetzt können wir den resultierenden Code ausführen.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняем один раз для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Das Programm sollte fehlerfrei laufen und in der Ausgabe sehen wir das Ausführungsprotokoll:

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=0

Großartig, wir haben die erste funktionierende Version des Smart Contracts geschrieben.

Jetzt müssen wir Funktionalität hinzufügen. Befassen wir uns zunächst mit den Nachrichten, die von der Außenwelt ankommen recv_external()

Der Entwickler selbst wählt das Nachrichtenformat, das der Vertrag akzeptieren kann.

Aber normalerweise

  • Erstens wollen wir unseren Vertrag vor der Außenwelt schützen und ihn so gestalten, dass nur der Eigentümer des Vertrags externe Nachrichten an ihn senden kann.
  • Zweitens: Wenn wir eine gültige Nachricht an TON senden, möchten wir, dass dies genau einmal geschieht, und wenn wir dieselbe Nachricht erneut senden, lehnt der Smart Contract sie ab.

Fast jeder Vertrag löst also diese beiden Probleme. Da unser Vertrag externe Nachrichten akzeptiert, müssen wir uns auch darum kümmern.

Wir machen es in umgekehrter Reihenfolge. Lösen wir zunächst das Problem mit der Wiederholung: Wenn der Vertrag eine solche Nachricht bereits empfangen und verarbeitet hat, wird er sie kein zweites Mal ausführen. Und dann werden wir das Problem lösen, sodass nur ein bestimmter Personenkreis Nachrichten an den Smart Contract senden kann.

Es gibt verschiedene Möglichkeiten, das Problem mit doppelten Nachrichten zu lösen. So machen wir es. Im Smart Contract initialisieren wir den Zähler der empfangenen Nachrichten mit dem Anfangswert 0. In jeder Nachricht an den Smart Contract fügen wir den aktuellen Zählerwert hinzu. Wenn der Zählerwert in der Nachricht nicht mit dem Wert im Smart Contract übereinstimmt, dann verarbeiten wir ihn nicht; wenn ja, dann verarbeiten wir ihn und erhöhen den Zähler im Smart Contract um 1.

Kehren wir zu zurück lottery-test-suite.fif und fügen Sie einen zweiten Test hinzu. Wenn wir eine falsche Nummer senden, sollte der Code eine Ausnahme auslösen. Lassen Sie beispielsweise die Vertragsdaten 166 speichern und wir senden 165.

<b 166 32 u, b> storage !
<b 165 32 u, b> message !

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx

drop 
exit_code ! 
."Exit code " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Not passed"

Lasst uns starten.

 ~/TON/build/crypto/fift -s lottery-test-suite.fif 

Und wir werden 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 Not passed
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Error interpreting file `lottery-test-suite.fif`: error interpreting included file `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 Not passed

In diesem Stadium lottery-test-suite.fif Sollte aussehen, wie Link.

Fügen wir nun die Gegenlogik zum Smart Contract hinzu lottery-code.fc.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementation 
}

() 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);
}

В slice in_msg ist die Botschaft, die wir senden.

Als erstes prüfen wir, ob die Nachricht Daten enthält. Wenn nicht, beenden wir einfach den Vorgang.

Als nächstes analysieren wir die Nachricht. in_msg~load_uint(32) lädt die Zahl 165, 32-Bit unsigned int aus der übermittelten Nachricht.

Als nächstes laden wir 32 Bit aus dem Smart-Contract-Speicher. Wir überprüfen, ob die geladene Nummer mit der übergebenen übereinstimmt. Wenn nicht, lösen wir eine Ausnahme aus. Da wir in unserem Fall eine Nichtübereinstimmung übergeben, sollte eine Ausnahme ausgelöst werden.

Jetzt kompilieren wir.

~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc 

Kopieren Sie den resultierenden Code nach lottery-test-suite.fif, vergessen Sie nicht, die letzte Zeile zu ersetzen.

Wir prüfen, ob der Test bestanden wird:

~/TON/build/crypto/fift -s lottery-test-suite.fif

Hier Sie können den entsprechenden Commit mit den aktuellen Ergebnissen sehen.

Beachten Sie, dass es unpraktisch ist, den kompilierten Code eines Smart Contracts ständig in eine Datei mit Tests zu kopieren. Deshalb schreiben wir ein Skript, das den Code für uns in eine Konstante schreibt, und verbinden den kompilierten Code einfach mit unseren Tests "include".

Erstellen Sie eine Datei im Projektordner 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.fc

Machen wir es ausführbar.

chmod +x ./build.sh

Führen Sie jetzt einfach unser Skript aus, um den Vertrag zu kompilieren. Aber außerdem müssen wir es in eine Konstante schreiben code. Also erstellen wir eine neue Datei lotter-compiled-for-test.fif, die wir in die Datei aufnehmen werden lottery-test-suite.fif.

Fügen wir sh Skirpt-Code hinzu, der die kompilierte Datei einfach dupliziert lotter-compiled-for-test.fif und ändern Sie die letzte Zeile darin.

# 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.fif

Lassen Sie uns nun zur Überprüfung das resultierende Skript ausführen und eine Datei wird generiert lottery-compiled-for-test.fif, die wir in unsere aufnehmen werden lottery-test-suite.fif

В lottery-test-suite.fif Löschen Sie den Vertragscode und fügen Sie die Zeile hinzu "lottery-compiled-for-test.fif" include.

Wir führen Tests durch, um zu überprüfen, ob sie bestehen.

~/TON/build/crypto/fift -s lottery-test-suite.fif

Großartig. Um nun den Start von Tests zu automatisieren, erstellen wir eine Datei test.sh, die zuerst ausgeführt wird build.sh, und führen Sie dann die Tests aus.

touch test.sh
chmod +x test.sh

Wir schreiben drinnen

./build.sh 

echo "nCompilation completedn"

export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fif

tun test.sh und führen Sie es aus, um sicherzustellen, dass die Tests funktionieren.

chmod +x ./test.sh
./test.sh

Wir prüfen, ob der Vertrag zustande kommt und die Tests durchgeführt werden.

Großartig, jetzt beim Start test.sh Die Tests werden sofort kompiliert und ausgeführt. Hier ist der Link dazu verpflichten.

Okay, bevor wir fortfahren, lassen Sie uns der Einfachheit halber noch etwas tun.

Lassen Sie uns einen Ordner erstellen build wo wir den kopierten Vertrag speichern und seinen Klon in eine Konstante schreiben lottery-compiled.fif, lottery-compiled-for-test.fif. Lassen Sie uns auch einen Ordner erstellen test Wo wird die Testdatei gespeichert? lottery-test-suite.fif und möglicherweise andere unterstützende Dateien. Link zu relevanten Änderungen.

Lassen Sie uns den Smart Contract weiterentwickeln.

Als nächstes sollte ein Test erfolgen, der überprüft, ob die Nachricht empfangen wurde und der Zähler im Geschäft aktualisiert wird, wenn wir die richtige Nummer senden. Aber das machen wir später.

Lassen Sie uns nun darüber nachdenken, welche Datenstruktur und welche Daten im Smart Contract gespeichert werden müssen.

Ich werde alles beschreiben, was wir speichern.

`seqno` 32-х битное целое положительное число счетчик. 

`pubkey` 256-ти битное целое положительное число публичный ключ, с помощью которого, мы будем проверять подпись отправленного извне сообщения, о чем ниже. 

`order_seqno` 32-х битное целое положительное число хранит счетчик количества ставок. 

`number_of_wins` 32-х битное целое положительное число хранит  количество побед. 

`incoming_amount` тип данных Gram (первые 4 бита отвечает за длину), хранит общее количество грамов, которые были отправлены на контртакт. 

`outgoing_amount` общее количество грамов, которое было отправлено победителям. 

`owner_wc` номер воркчейна, 32-х битное (в некоторых местах написано, что 8-ми битное) целое число. В данный момент всего два -1 и 0. 

`owner_account_id` 256-ти битное целое положительное число, адрес контракта в текущем воркчейне. 

`orders` переменная типа словарь, хранит последние двадцать ставок. 

Als nächstes müssen Sie zwei Funktionen schreiben. Rufen wir den ersten an pack_state(), wodurch die Daten für die spätere Speicherung im Smart-Contract-Speicher gepackt werden. Nennen wir den zweiten unpack_state() liest Daten aus dem Speicher und gibt sie zurück.

_ 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 klappt so Zwischenergebnis.

Um Daten zu speichern, müssen Sie die integrierte Funktion aufrufen set_data() und es werden Daten von geschrieben pack_state() im Smart-Contract-Speicher.

cell packed_state = pack_state(arg_1, .., arg_n); 
set_data(packed_state);

Da uns nun komfortable Funktionen zum Schreiben und Lesen von Daten zur Verfügung stehen, können wir weitermachen.

Wir müssen überprüfen, ob die von außen eingehende Nachricht vom Eigentümer des Vertrags (oder einem anderen Benutzer, der Zugriff auf den privaten Schlüssel hat) signiert ist.

Wenn wir einen Smart Contract veröffentlichen, können wir ihn mit den benötigten Daten im Speicher initialisieren, die für die zukünftige Verwendung gespeichert werden. Wir werden dort den öffentlichen Schlüssel aufzeichnen, damit wir überprüfen können, ob die eingehende Nachricht mit dem entsprechenden privaten Schlüssel signiert wurde.

Bevor wir fortfahren, erstellen wir einen privaten Schlüssel und schreiben ihn darauf test/keys/owner.pk. Dazu starten wir Fift im interaktiven Modus und führen vier Befehle aus.

`newkeypair` генерация публичного и приватного ключа и запись их в стек. 

`drop` удаления из стека верхнего элемента (в данном случае публичный ключ)  

`.s` просто посмотреть что лежит в стеке в данный момент 

`"owner.pk" B>file` запись приватного ключа в файл с именем `owner.pk`. 

`bye` завершает работу с Fift. 

Lassen Sie uns einen Ordner erstellen keys im Ordner test und schreiben Sie dort den privaten Schlüssel.

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
bye

Wir sehen eine Datei im aktuellen Ordner owner.pk.

Wir entfernen den öffentlichen Schlüssel vom Stapel und können ihn bei Bedarf vom privaten Schlüssel abrufen.

Jetzt müssen wir eine Signaturüberprüfung schreiben. Beginnen wir mit dem Test. Zuerst lesen wir mit der Funktion den privaten Schlüssel aus der Datei file>B und schreibe es in eine Variable owner_private_key, dann mit der Funktion priv>pub Wandeln Sie den privaten Schlüssel in einen öffentlichen Schlüssel um und schreiben Sie das Ergebnis hinein 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 Smart-Contract-Speicher mit beliebigen Daten in der gleichen Reihenfolge wie in der Funktion pack_state()und schreibe es in eine Variable storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

"./keys/owner.pk" file>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, 0 32 u, 0 32 u, 0 Gram, 0 Gram, owner_wc @ 32 i, owner_account_id @ 256 u,  orders @ dict, b> storage !

Als nächstes verfassen wir eine signierte Nachricht, sie enthält nur die Signatur und den Zählerwert.

Zuerst erstellen wir die Daten, die wir übertragen möchten, dann signieren wir sie mit einem privaten Schlüssel und generieren schließlich eine signierte Nachricht.

variable message_to_sign
variable message_to_send
variable signature
<b 0 32 u, b> message_to_sign !
message_to_sign @ hashu owner_private_key @ ed25519_sign_uint signature !
<b signature @ B, 0 32 u, b> <s  message_to_send !  

Dadurch wird die Nachricht, die wir an den Smart Contract senden, in einer Variablen aufgezeichnet message_to_send, über Funktionen hashu, ed25519_sign_uint du kannst lesen in der Fift-Dokumentation.

Und um den Test durchzuführen, rufen wir erneut an.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Folgendermaßen Die Datei mit den Tests sollte zu diesem Zeitpunkt so aussehen.

Lassen Sie uns den Test ausführen und er wird fehlschlagen. Deshalb ändern wir den Smart Contract so, dass er Nachrichten dieses Formats empfangen und die Signatur überprüfen kann.

Zuerst zählen wir 512 Bits der Signatur aus der Nachricht und schreiben sie in eine Variable, dann zählen wir 32 Bits der Zählervariablen.

Da wir über eine Funktion zum Auslesen von Daten aus dem Smart Contract Storage verfügen, werden wir diese nutzen.

Als nächstes erfolgt die Prüfung des mit der Speicherung übergebenen Zählers und die Prüfung der Signatur. Wenn etwas nicht übereinstimmt, lösen wir eine Ausnahme mit dem entsprechenden Code aus.

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));

Relevanter Commit hierher.

Lassen Sie uns die Tests ausführen und feststellen, dass der zweite Test fehlschlägt. Aus zwei Gründen sind nicht genügend Bits in der Nachricht und nicht genügend Bits im Speicher vorhanden, sodass der Code beim Parsen abstürzt. Wir müssen der Nachricht, die wir senden, eine Signatur hinzufügen und den Speicher vom letzten Test kopieren.

Im zweiten Test werden wir eine Nachrichtensignatur hinzufügen und den Smart-Contract-Speicher ändern. Folgendermaßen So sieht die Datei mit den Tests im Moment aus.

Schreiben wir einen vierten Test, bei dem wir eine Nachricht senden, die mit dem privaten Schlüssel einer anderen Person signiert ist. Lassen Sie uns einen weiteren privaten Schlüssel erstellen und ihn in einer Datei speichern not-owner.pk. Mit diesem privaten Schlüssel signieren wir die Nachricht. Lassen Sie uns die Tests durchführen und sicherstellen, dass alle Tests erfolgreich sind. Begehen im Moment.

Jetzt können wir endlich mit der Implementierung der Smart-Contract-Logik fortfahren.
В recv_external() Wir akzeptieren zwei Arten von Nachrichten.

Da unser Vertrag die Verluste der Spieler ansammelt, muss dieses Geld an den Ersteller der Lotterie überwiesen werden. Die Wallet-Adresse des Lotterie-Erstellers wird bei der Vertragserstellung im Speicher erfasst.

Für alle Fälle benötigen wir die Möglichkeit, die Adresse zu ändern, an die wir Gramm der Verlierer senden. Wir sollten auch in der Lage sein, Gramm aus der Lotterie an die Adresse des Besitzers zu senden.

Beginnen wir mit dem ersten. Schreiben wir zunächst einen Test, der überprüft, ob der Smart Contract nach dem Senden der Nachricht die neue Adresse im Speicher gespeichert hat. Bitte beachten Sie, dass wir in der Nachricht neben dem Zähler und der neuen Adresse auch übermitteln action Eine nicht negative 7-Bit-Ganzzahl. Abhängig davon entscheiden wir, wie die Nachricht im Smart Contract verarbeitet werden soll.

<b 0 32 u, 1 @ 7 u, new_owner_wc @  32 i, new_owner_account_id @ 256 u, b> message_to_sign !

Im Test können Sie sehen, wie der Smartcontract-Speicher deserialisiert wird storage in Fünf. Die Deserialisierung von Variablen wird in der Fift-Dokumentation beschrieben.

Commit-Link mit Teigzusatz.

Lassen Sie uns den Test ausführen und sicherstellen, dass er fehlschlägt. Fügen wir nun eine Logik hinzu, um die Adresse des Lotteriebesitzers zu ändern.

Im Smart Contract analysieren wir weiter message, reinlesen action. Wir möchten Sie daran erinnern, dass wir zwei haben werden action: Adresse ändern und Gramm senden.

Anschließend lesen wir die neue Adresse des Vertragsinhabers aus und speichern diese im Speicher.
Wir führen die Tests durch und stellen fest, dass der dritte Test fehlschlägt. Es stürzt ab, weil der Vertrag nun zusätzlich 7 Bits aus der Nachricht analysiert, die im Test fehlen. Fügen Sie der Nachricht ein nicht vorhandenes hinzu action. Lassen Sie uns die Tests durchführen und sicherstellen, dass alles erfolgreich ist. Hier sich zu Veränderungen verpflichten. Großartig.

Schreiben wir nun die Logik zum Senden der angegebenen Grammzahl an die zuvor gespeicherte Adresse.

Schreiben wir zunächst einen Test. Wir werden zwei Tests schreiben, einen, wenn das Gleichgewicht nicht ausreicht, und den zweiten, wenn alles erfolgreich verlaufen sollte. Tests können eingesehen werden in diesem Commit.

Jetzt fügen wir den Code hinzu. Schreiben wir zunächst zwei Hilfsmethoden. Die erste Get-Methode besteht darin, den aktuellen Kontostand eines Smart Contracts herauszufinden.

int balance() inline_ref method_id {
    return get_balance().pair_first();
}

Und der zweite dient zum Senden von Gramm an einen anderen Smart Contract. Ich habe diese Methode vollständig von 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 zeroes +  0 as an indicator that there is no cell with the data.
        .end_cell(); 
    send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}

Fügen wir diese beiden Methoden zum Smart Contract hinzu und schreiben wir die Logik. Zuerst analysieren wir die Grammzahl aus der Nachricht. Als nächstes überprüfen wir den Kontostand. Wenn dieser nicht ausreicht, lösen wir eine Ausnahme aus. Wenn alles in Ordnung 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));

Folgendermaßen sieht im Moment nach einem Smart Contract aus. Lassen Sie uns die Tests durchführen und sicherstellen, dass sie bestehen.

Übrigens wird für jede verarbeitete Nachricht eine Provision vom Smart Contract abgezogen. Damit die Smart-Contract-Nachrichten die Anfrage ausführen können, müssen Sie nach grundlegenden Prüfungen anrufen accept_message().

Kommen wir nun zu den internen Nachrichten. Tatsächlich akzeptieren wir nur Gramm und senden dem Spieler den doppelten Betrag zurück, wenn er gewinnt, und ein Drittel an den Besitzer, wenn er verliert.

Schreiben wir zunächst einen einfachen Test. Dazu benötigen wir eine Testadresse des Smart Contracts, von der aus wir angeblich Gramm an den Smart Contract senden.

Die Smart-Contract-Adresse besteht aus zwei Zahlen, einer 32-Bit-Ganzzahl, die für die Arbeitskette verantwortlich ist, und einer 256-Bit-nicht-negativen Ganzzahl, die in dieser Arbeitskette eindeutig ist. Zum Beispiel -1 und 12345. Dies ist die Adresse, die wir in einer Datei speichern.

Ich habe die Funktion zum Speichern der Adresse kopiert TonUtil.fif.

// ( wc addr fname -- )  Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address

Schauen wir uns an, wie die Funktion funktioniert. Dadurch erhalten Sie ein Verständnis dafür, wie Fift funktioniert. Starten Sie Fift im interaktiven Modus.

~/TON/build/crypto/fift -i 

Zuerst schieben wir -1, 12345 und den Namen der zukünftigen Datei „sender.addr“ auf den Stack:

-1 12345 "sender.addr" 

Der nächste Schritt besteht darin, die Funktion auszuführen -rot, wodurch der Stapel so verschoben wird, dass sich oben auf dem Stapel eine eindeutige Smart-Contract-Nummer befindet:

"sender.addr" -1 12345

256 u>B Konvertiert eine nichtnegative 256-Bit-Ganzzahl in Bytes.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap vertauscht die beiden obersten Elemente des Stapels.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B wandelt eine 32-Bit-Ganzzahl in Bytes um.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ verbindet zwei Bytesequenzen.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Wieder swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Und schließlich werden die Bytes in die Datei geschrieben B>file. Danach ist unser Stapel leer. Wir hören auf Fift. Im aktuellen Ordner wurde eine Datei erstellt sender.addr. Verschieben wir die Datei in den erstellten Ordner test/addresses/.

Schreiben wir einen einfachen Test, der Gramm an einen Smart Contract sendet. Hier ist der Commit.

Schauen wir uns nun die Logik der Lotterie an.

Als erstes überprüfen wir die Nachricht bounced oder nicht, wenn bounced, dann ignorieren wir es. bounced bedeutet, dass der Vertrag Gramm zurückgibt, wenn ein Fehler auftritt. Wir geben keine Gramm zurück, wenn plötzlich ein Fehler auftritt.

Wir prüfen, ob der Restbetrag weniger als ein halbes Gramm beträgt, dann akzeptieren wir die Meldung einfach und ignorieren sie.

Als nächstes analysieren wir die Adresse des Smart Contracts, von dem die Nachricht stammt.

Wir lesen die Daten aus dem Speicher aus und löschen dann alte Wetten aus der Historie, wenn es mehr als zwanzig sind. Der Einfachheit halber habe ich drei zusätzliche Funktionen geschrieben pack_order(), unpack_order(), remove_old_orders().

Als nächstes prüfen wir, ob der Restbetrag für die Zahlung nicht ausreicht, dann gehen wir davon aus, dass es sich nicht um eine Wette, sondern um eine Auffüllung handelt und speichern die Auffüllung in orders.

Dann endlich die Essenz des Smart Contracts.

Wenn der Spieler verliert, speichern wir ihn zunächst im Wettverlauf und wenn der Betrag mehr als 3 Gramm beträgt, senden wir 1/3 an den Eigentümer des Smart Contracts.

Wenn der Spieler gewinnt, senden wir den doppelten Betrag an die Adresse des Spielers und speichern dann die Informationen über die Wette im Verlauf.

() 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() < 2 * order_amount + 500000000) { ;; not enough grams to pay the bet back, so this is re-fill
        builder order = pack_order(order_seqno, 1, 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));
        return ();
    }
    if (rand(10) >= 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 war's. Entsprechender Commit.

Jetzt müssen wir nur noch einfache Get-Methoden erstellen, damit wir Informationen über den Status des Vertrags von der Außenwelt erhalten (tatsächlich die Daten aus ihrem Smart-Contract-Speicher lesen können).

Fügen wir Get-Methoden hinzu. Im Folgenden erfahren Sie, wie Sie Informationen zu einem Smart Contract erhalten.

Ich habe auch vergessen, den Code hinzuzufügen, der die allererste Anfrage verarbeitet, die beim Veröffentlichen eines Smart Contracts auftritt. Entsprechender Commit. Und weiter behoben Fehler beim Senden von 1/3 des Betrags auf das Konto des Eigentümers.

Der nächste Schritt besteht darin, den Smart Contract zu veröffentlichen. Lassen Sie uns einen Ordner erstellen requests.

Ich habe den Veröffentlichungscode als Grundlage genommen simple-wallet-code.fc was Sie können finden im offiziellen Repository.

Etwas, auf das es sich zu achten lohnt. Wir generieren einen Smart-Contract-Speicher und eine Eingabenachricht. Danach wird die Adresse des Smart Contracts generiert, d. h. die Adresse ist bereits vor der Veröffentlichung in TON bekannt. Dann müssen Sie mehrere Gramm an diese Adresse senden und erst danach eine Datei mit dem Smart Contract selbst senden, da das Netzwerk eine Provision für die Speicherung des Smart Contracts und der darin enthaltenen Vorgänge (Validatoren, die Smart Contracts speichern und ausführen) verlangt ). Der Code kann hier eingesehen werden.

Als nächstes führen wir den Veröffentlichungscode aus und erhalten lottery-query.boc Smart-Contract-Datei und -Adresse.

~/TON/build/crypto/fift -s requests/new-lottery.fif 0

Vergessen 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.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Saving address to file lottery.addr)
Non-bounceable address (for init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable address (for later access): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

Lasst uns zum Spaß eine Anfrage an TON richten

$ ./lite-client/lite-client -C ton-lite-client-test1.config.json 
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Und wir werden sehen, dass das Konto mit dieser Adresse leer ist.

account state is empty

Wir versenden an die Adresse 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gramm und nach ein paar Sekunden führen wir den gleichen Befehl aus. Zum Versenden von Gramm verwende ich offizielles Portemonnaie, und Sie können jemanden aus dem Chat nach Testgrammen fragen, worüber ich am Ende des Artikels sprechen werde.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Sieht aus wie ein nicht initialisiertes (state:account_uninit) ein Smart Contract mit derselben Adresse und einem Saldo von 1 Nanogramm.

account state is (account
  addr:(addr_std
    anycast:nothing workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
  storage_stat:(storage_info
    used:(storage_used
      cells:(var_uint len:1 value:1)
      bits:(var_uint len:1 value:103)
      public_cells:(var_uint len:0 value:0)) last_paid:1583257959
    due_payment:nothing)
  storage:(account_storage last_trans_lt:3825478000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:2000000000))
      other:(extra_currencies
        dict:hme_empty))
    state:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
last transaction lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
account balance is 2000000000ng

Lassen Sie uns nun den Smart Contract veröffentlichen. Lassen Sie uns den Lite-Client starten und ausführen.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] sending query from file lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    external message status is 1 

Überprüfen wir, ob der Vertrag veröffentlicht wurde.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Unter anderem bekommen wir.

  storage:(account_storage last_trans_lt:3825499000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:1987150999))
      other:(extra_currencies
        dict:hme_empty))
    state:(account_active

Wir sehen das account_active.

Entsprechender Commit mit Änderungen hierher.

Lassen Sie uns nun Anfragen für die Interaktion mit dem Smart Contract erstellen.

Genauer gesagt belassen wir den ersten Schritt zum Ändern der Adresse als eigenständige Arbeit und machen den zweiten Schritt zum Versenden von Grammatiken an die Adresse des Eigentümers. Tatsächlich müssen wir dasselbe tun wie im Test zum Senden von Gramm.

Dies ist die Nachricht, die wir an den Smart-Vertrag senden msg_seqno 165, action 2 und 9.5 Gramm zum Versenden.

<b 165 32 u, 2 7 u, 9500000000 Gram, b>

Vergessen Sie nicht, die Nachricht mit Ihrem privaten Schlüssel zu signieren lottery.pk, das zuvor beim Erstellen des Smart Contracts generiert wurde. Hier ist der entsprechende Commit.

Empfangen von Informationen aus einem Smart Contract mithilfe von Get-Methoden

Schauen wir uns nun an, wie man Smart-Contract-Get-Methoden ausführt.

Rennen lite-client und führen Sie 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 ] 
...

В result enthält den Wert, den die Funktion zurückgibt balance() aus unserem Smart Contract.
Wir werden dasselbe für mehrere weitere Methoden tun.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments:  [ 77871 ] 
result:  [ 1 ] 

Lassen Sie uns nach Ihrem Wettverlauf fragen.

> 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 Lite-Client- und Get-Methoden verwenden, um Informationen über den Smart Contract auf der Website anzuzeigen.

Anzeige von Smart-Contract-Daten auf der Website

Ich habe eine einfache Website in Python geschrieben, um die Daten aus dem Smart Contract auf bequeme Weise anzuzeigen. Hier werde ich nicht näher darauf eingehen und die Seite veröffentlichen in einem Commit.

Anfragen an TON werden gestellt von Python über lite-client. Der Einfachheit halber wird die Website in Docker gepackt und in Google Cloud veröffentlicht. Verknüpfung.

Lass es uns versuchen

Versuchen wir nun, Gramm zur Auffüllung dorthin zu schicken Brieftasche. Wir versenden 40 Gramm. Und lassen Sie uns der Klarheit halber ein paar Wetten abschließen. Wir sehen, dass die Website den Verlauf der Wetten, den aktuellen Gewinnprozentsatz und andere nützliche Informationen anzeigt.

Wir sehendass wir das erste gewonnen und das zweite verloren haben.

Nachwort

Es stellte sich heraus, dass der Artikel viel länger war, als ich erwartet hatte, vielleicht hätte er kürzer sein können, oder vielleicht nur für eine Person, die nichts über TON weiß und einen nicht ganz so einfachen Smart Contract mit der Möglichkeit zur Interaktion schreiben und veröffentlichen möchte Es. Vielleicht hätte man manches einfacher erklären können.

Vielleicht hätten einige Aspekte der Umsetzung effizienter und eleganter erfolgen können, aber dann hätte die Erstellung des Artikels noch mehr Zeit in Anspruch genommen. Es ist auch möglich, dass ich irgendwo einen Fehler gemacht oder etwas nicht verstanden habe. Wenn Sie also etwas Ernstes tun, müssen Sie sich auf die offizielle Dokumentation oder das offizielle Repository mit dem TON-Code verlassen.

Es sollte beachtet werden, dass, da sich TON selbst noch in der aktiven Entwicklungsphase befindet, Änderungen auftreten können, die einen der Schritte in diesem Artikel zum Scheitern bringen (was passiert ist, während ich geschrieben habe, es wurde bereits korrigiert), aber der allgemeine Ansatz ist so unwahrscheinlich, dass sich etwas ändert.

Ich werde nicht über die Zukunft von TON sprechen. Vielleicht wird die Plattform zu etwas Großem, und wir sollten uns jetzt die Zeit nehmen, sie zu studieren und mit unseren Produkten eine Nische zu füllen.

Es gibt auch Libra von Facebook, dessen potenzielles Nutzerpublikum größer als TON ist. Ich weiß fast nichts über Libra, dem Forum nach zu urteilen, gibt es dort viel mehr Aktivität als in der TON-Community. Obwohl die Entwickler und die Community von TON eher Underground sind, ist das auch cool.

Referenzen

  1. Offizielle TON-Dokumentation: https://test.ton.org
  2. Offizielles TON-Repository: https://github.com/ton-blockchain/ton
  3. Offizielles Wallet für verschiedene Plattformen: https://wallet.ton.org
  4. Smart-Contract-Repository aus diesem Artikel: https://github.com/raiym/astonished
  5. Link zur Smart-Contract-Website: https://ton-lottery.appspot.com
  6. Repository für die Erweiterung für Visual Studio Code für FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Chatten Sie über TON im Telegram, was mir in der Anfangsphase wirklich geholfen hat, es herauszufinden. Ich denke, es ist kein Fehler, wenn ich sage, dass jeder da ist, der etwas für TON geschrieben hat. Dort können Sie auch Testgramme anfordern. https://t.me/tondev_ru
  8. Ein weiterer Chat über TON, in dem ich nützliche Informationen gefunden habe: https://t.me/TONgramDev
  9. Erste Phase des Wettbewerbs: https://contest.com/blockchain
  10. Zweite Phase des Wettbewerbs: https://contest.com/blockchain-2

Source: habr.com

Kommentar hinzufügen