À propos de la rédaction et de la publication d'un contrat intelligent dans le Telegram Open Network (TON)

À propos de la rédaction et de la publication d'un contrat intelligent dans TON

De quoi parle cet article?

Dans l'article, je parlerai de la façon dont j'ai participé au premier (des deux) concours de blockchain Telegram, n'ai pas remporté de prix et décidé d'enregistrer l'expérience dans l'article afin qu'il ne sombre pas dans l'oubli et, peut-être, aide quelqu'un.

Comme je ne voulais pas écrire de code abstrait, mais faire quelque chose qui fonctionne, j'ai écrit pour l'article un contrat intelligent de loterie instantanée et un site Web qui affiche les données de contrat intelligent directement à partir de TON sans utiliser de stockage intermédiaire.

L'article sera utile pour ceux qui veulent faire leur premier contrat intelligent dans TON, mais ne savent pas par où commencer.

En prenant l'exemple d'une loterie, je vais passer de la mise en place de l'environnement à la publication d'un contrat intelligent, interagir avec lui, et écrire un site pour recevoir et publier des données.

A propos de la participation au concours

En octobre dernier, Telegram a annoncé un concours blockchain avec de nouvelles langues Fift и FunC. Il fallait choisir d'écrire l'un des cinq contrats intelligents proposés. J'ai pensé que ce serait bien de faire quelque chose qui sort de l'ordinaire, apprendre une langue et faire quelque chose, même si je n'ai rien à écrire d'autre à l'avenir. De plus, le sujet est constamment à l'écoute.

Cela vaut la peine de dire que je n'avais aucune expérience dans le développement de contrats intelligents.

J'avais prévu de participer jusqu'à la toute fin, tant que cela se révélerait, puis d'écrire un article de critique, mais j'ai immédiatement échoué au premier. je portefeuille écrit avec multi-signature sur FunC et il travaillait généralement. Pris comme base contrat intelligent sur la Solidité.

À ce moment-là, je pensais que c'était certainement suffisant pour prendre au moins une place primée. En conséquence, environ 40 participants sur 60 sont devenus des gagnants et je n'étais pas parmi eux. En général, ce n'est rien de terrible, mais une chose m'a tendu. Au moment de l'annonce des résultats, un bilan avec un test pour mon contrat n'avait pas été fait, j'ai demandé aux participants au chat s'il y avait quelqu'un d'autre qui ne l'avait pas, il n'y en avait pas.

Apparemment, après avoir prêté attention à mes messages, deux jours plus tard, les juges ont publié un commentaire et je ne comprenais toujours pas, ils ont accidentellement manqué mon contrat intelligent lors du jugement, ou ont simplement considéré qu'il était si mauvais qu'il n'avait pas besoin de commentaire. J'ai posé une question sur la page, mais je n'ai pas reçu de réponse. Bien que qui a jugé n'est pas un secret, j'ai considéré qu'il était superflu d'écrire des messages personnels.

Beaucoup de temps a été consacré à la compréhension, il a donc été décidé d'écrire un article. Comme il n'y a pas encore beaucoup d'informations, l'article aidera à faire gagner du temps à toutes les personnes intéressées.

Le concept de contrats intelligents dans TON

Avant d'écrire quelque chose, vous devez déterminer de quel côté aborder cette chose. Par conséquent, je vais maintenant vous dire en quoi consiste le système. Plus précisément, quelles parties devez-vous connaître pour rédiger au moins une sorte de contrat de travail.

Nous nous concentrerons sur la rédaction d'un contrat intelligent et travaillerons avec TON Virtual Machine (TVM), Fift и FunC, donc l'article ressemble plus à une description du développement d'un programme conventionnel. Nous ne nous attarderons pas ici sur le fonctionnement de la plateforme elle-même.

Généralement sur la façon dont cela fonctionne TVM et la langue Fift il y a une bonne documentation officielle. Lors de ma participation au concours et maintenant lors de la rédaction du contrat actuel, je me suis souvent tournée vers elle.

Le langage principal dans lequel les contrats intelligents sont rédigés est FunC. Il n'y a pas de documentation à ce sujet pour le moment, donc pour écrire quelque chose, vous devez étudier des exemples de contrats intelligents du référentiel officiel et l'implémentation du langage lui-même là-bas, et vous pouvez regarder des exemples de contrats intelligents pour les deux derniers concours. Liens en fin d'article.

Supposons que nous ayons déjà écrit un contrat intelligent pour FunC, après cela, nous compilons le code dans l'assembleur Fift.

Le contrat intelligent compilé reste à publier. Pour ce faire, vous devez écrire une fonction pour Fift, qui prendra le code du contrat intelligent et quelques autres paramètres en entrée, et la sortie sera un fichier avec l'extension .boc (qui signifie "sac de cellules"), et, selon la façon dont nous l'écrivons, une clé privée et une adresse générée sur la base du code de contrat intelligent. Les grammes peuvent déjà être envoyés à une adresse de contrat intelligent qui n'a pas encore été publiée.

Pour publier un contrat intelligent dans TON reçu .boc le fichier devra être envoyé à la blockchain à l'aide d'un client léger (plus de détails ci-dessous). Mais avant de publier, vous devez transférer des grammes à l'adresse générée, sinon le contrat intelligent ne sera pas publié. Après publication, il sera possible d'interagir avec un contrat intelligent en lui envoyant des messages de l'extérieur (par exemple, en utilisant un client léger) ou de l'intérieur (par exemple, un contrat intelligent envoie un message à un autre à l'intérieur de TON).

Une fois que nous avons compris comment le code est publié, cela devient plus facile. Nous savons à peu près ce que nous voulons écrire et comment notre programme fonctionnera. Et en écrivant, nous recherchons comment il est déjà implémenté dans les contrats intelligents existants, ou nous examinons le code d'implémentation Fift и FunC dans le dépôt officiel, ou regardez dans la documentation officielle.

Très souvent, j'ai recherché des mots-clés dans le chat Telegram où tous les participants du concours et les employés de Telegram, y compris, se sont réunis, il s'est avéré que pendant le concours, tout le monde s'y est réuni et a commencé à discuter de Fift et de FunC. Lien en fin d'article.

Il est temps de passer de la théorie à la pratique.

Préparer l'environnement pour travailler avec TON

Tout ce qui sera décrit dans l'article que j'ai fait sur MacOS et revérifié dans un Ubuntu 18.04 LTS propre sur Docker.

La première chose à faire est de télécharger et d'installer lite-client avec lequel vous pouvez envoyer des demandes à TON.

Les instructions sur le site officiel décrivent le processus d'installation en détail et clairement et omettent certains détails. Ici, nous suivons les instructions tout au long du processus, en installant les dépendances manquantes. Je n'ai pas compilé chaque projet moi-même et installé à partir du référentiel officiel Ubuntu (sur MacOS, j'ai utilisé 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 

Une fois toutes les dépendances installées, vous pouvez installer lite-client, Fift, FunC.

Tout d'abord, nous clonons le référentiel TON avec les dépendances. Pour plus de commodité, nous ferons tout dans un dossier ~/TON.

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

Le référentiel stocke également les implémentations Fift и FunC.

Nous sommes maintenant prêts à construire le projet. Code du référentiel cloné dans le dossier ~/TON/ton. la ~/TON créer un dossier build et récupérez le projet dedans.

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

Puisque nous allons écrire un contrat intelligent, nous devons non seulement lite-clientMais Fift с FunC, donc nous compilons tout. Pas un processus rapide donc nous attendons.

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

Ensuite, téléchargez le fichier de configuration, qui contient des données sur le nœud auquel lite-client se connectera.

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

Faire les premières requêtes en TON

Maintenant courons lite-client.

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

Si la construction a réussi, après le lancement, vous verrez le journal de connexion du client léger au nœud.

[ 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)
...

Vous pouvez exécuter la commande help et voir quelles commandes sont disponibles.

help

Listons les commandes que nous utiliserons dans cet article.

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-методы смартконтракта. 

Nous sommes maintenant prêts à rédiger le contrat lui-même.

exécution

Idée

Comme je l'ai écrit plus haut, le contrat intelligent que nous écrivons est une loterie.

De plus, ce n'est pas une loterie dans laquelle vous devez acheter un billet et attendre une heure, un jour ou un mois, mais une loterie instantanée dans laquelle l'utilisateur transfère à l'adresse du contrat N grammes, et récupère instantanément 2 * N grammes ou perd. Nous ferons en sorte que la probabilité de gagner soit d'environ 40 %. S'il n'y a pas assez de grammes pour le paiement, nous considérerons la transaction comme un réapprovisionnement.

De plus, il est important que les paris puissent être vus en temps réel et de manière pratique, afin que l'utilisateur puisse immédiatement comprendre s'il a gagné ou perdu. Par conséquent, vous devez créer un site Web qui affichera les tarifs et le résultat directement à partir de TON.

Rédiger un contrat intelligent

Pour plus de commodité, j'ai mis en surbrillance le code pour FunC, le plugin peut être trouvé et installé dans la recherche de Visual Studio Code, si vous voulez soudainement ajouter quelque chose, alors j'ai posté le plugin dans le domaine public. De plus, quelqu'un a déjà créé un plugin pour travailler avec Fift, vous pouvez également l'installer et le trouver dans VSC.

Créez immédiatement un référentiel où nous validerons les résultats intermédiaires.

Pour nous faciliter la vie, nous rédigerons un contrat intelligent et le testerons localement jusqu'à ce qu'il soit prêt. Ce n'est qu'après cela que nous le publierons dans TON.

Le contrat intelligent a deux méthodes externes accessibles. D'abord, recv_external() cette fonction est exécutée lorsqu'une demande au contrat provient du monde extérieur, c'est-à-dire pas de TON, par exemple, lorsque nous formons nous-mêmes un message et l'envoyons via lite-client. Deuxième, recv_internal() c'est quand, à l'intérieur du TON lui-même, tout contrat fait référence au nôtre. Dans les deux cas, vous pouvez passer des paramètres à la fonction.

Commençons par un exemple simple qui fonctionnera s'il est publié, mais ne contient aucune charge fonctionnelle.

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

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

Il faut ici expliquer ce que slice. Toutes les données stockées dans TON Blockchain sont une collection TVM cell ou simplement cell, une telle cellule peut stocker jusqu'à 1023 bits de données et jusqu'à 4 références à d'autres cellules.

TVM cell slice ou slice fait partie de l'existant cell est utilisé pour son analyse, il sera clair plus loin. L'essentiel pour nous est que nous puissions passer à un contrat intelligent slice et, selon le type de message, traiter les données dans recv_external() ou recv_internal().

impure — un mot-clé qui indique que la fonction modifie les données du contrat intelligent.

Enregistrez le code du contrat dans lottery-code.fc et compiler.

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

La valeur des drapeaux peut être visualisée à l'aide de la commande

~/TON/build/crypto/func -help

Nous avons compilé le code assembleur Fift dans 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

Il peut être exécuté localement, pour cela nous préparerons l'environnement.

Notez que la première ligne relie Asm.fif, c'est du code écrit en assembleur Fift pour Fift.

Puisque nous voulons exécuter et tester le contrat intelligent, nous allons créer un fichier localement lottery-test-suite.fif et copiez-y le code compilé, en remplaçant la dernière ligne de celui-ci, qui écrit le code du contrat intelligent dans une constante codepour ensuite le passer à la machine virtuelle :

"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

Bien que cela semble clair, ajoutons maintenant le code que nous utiliserons pour lancer TVM dans le même fichier.

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 nous écrivons le contexte, c'est-à-dire les données avec lesquelles le TVM (ou l'état du réseau) sera lancé. Même pendant la compétition, l'un des développeurs a montré comment créer c7 et j'ai copié. Dans cet article, nous devrons peut-être modifier rand_seed puisque la génération d'un nombre aléatoire en dépend et ne change pas, le même nombre sera retourné à chaque fois.

recv_internal и recv_external les constantes avec une valeur de 0 et -1 seront chargées d'appeler les fonctions appropriées dans le contrat intelligent.

Nous sommes maintenant prêts à créer le premier test pour notre contrat intelligent vide. Pour plus de clarté, pour l'instant, nous allons ajouter tous les tests dans le même fichier. lottery-test-suite.fif.

Créons une variable storage et écrire un vide cell, ce sera le stockage du contrat intelligent.

message c'est le message que nous enverrons au contact intelligent de l'extérieur. Laissons-le vide pour l'instant.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Après avoir préparé les constantes et les variables, nous démarrons TVM en utilisant la commande runvmctx et passez les paramètres créés à l'entrée.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

En conséquence, nous réussirons comme ça code intermédiaire à Fift.

Nous pouvons maintenant exécuter le code résultant.

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

Le programme devrait fonctionner sans erreurs et dans la sortie nous verrons le journal d'exécution :

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

Super, nous avons écrit la première version de travail du contrat intelligent.

Maintenant, nous devons ajouter des fonctionnalités. Traitons d'abord des messages qui viennent du monde extérieur pour recv_external()

Le développeur choisit lui-même le format de message que le contrat peut accepter.

Mais habituellement

  • Premièrement, nous voulons protéger notre contrat du monde extérieur et faire en sorte que seul le propriétaire du contrat puisse lui envoyer des messages externes.
  • deuxièmement, lorsque nous envoyons un message valide à TON, nous voulons que cela se produise exactement une fois et lorsque le même message est envoyé à nouveau, le contrat intelligent le rejette.

Par conséquent, dans presque tous les contrats, ces deux problèmes sont résolus, puisque notre contrat accepte les messages externes, nous devons également nous en occuper.

Nous allons procéder dans l'ordre inverse. Premièrement, nous résolvons le problème de la répétition, si le contrat a déjà reçu un tel message et l'a traité, alors il ne l'exécutera pas une seconde fois. Et puis nous résoudrons le problème afin que seul un certain cercle de personnes puisse envoyer des messages au contrat intelligent.

Il existe différentes façons de résoudre le problème des messages répétés. Nous allons procéder ainsi. Dans le contrat intelligent, nous initialisons le compteur de messages reçus avec une valeur initiale de 0. Dans chaque message au contrat intelligent, nous ajouterons la valeur actuelle du compteur. Si la valeur du compteur dans le message ne correspond pas à la valeur dans le contrat intelligent, nous ne le traitons pas, si c'est le cas, nous traitons et augmentons le compteur dans le contrat intelligent de 1.

Nous revenons à lottery-test-suite.fif et ajoutez-y le deuxième test. Envoyons un numéro invalide, le code devrait lever une exception. Par exemple, disons que 166 est stocké dans les données du contrat, et nous enverrons 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"

Courons.

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

Et nous verrons que le test est exécuté avec une erreur.

[ 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

À ce stade lottery-test-suite.fif devrait ressembler lien.

Ajoutons maintenant la logique de compteur au contrat intelligent dans 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 se trouve le message que nous envoyons.

La première chose que nous faisons est de vérifier s'il y a des données dans le message, sinon, nous quittons simplement.

Ensuite, nous analysons le message. in_msg~load_uint(32) charge le numéro 165, 32 bits unsigned int du message transmis.

Ensuite, nous chargeons 32 bits à partir du stockage de contrat intelligent. Nous vérifions que le numéro chargé correspond à celui passé, sinon nous levons une exception. Dans notre cas, puisque nous passons une incompatibilité, une exception doit être levée.

Compilons maintenant.

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

Copiez le code obtenu dans lottery-test-suite.fif, sans oublier de remplacer la dernière ligne.

Vérification de la réussite du test :

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

Ici vous pouvez voir le commit correspondant avec les résultats actuels.

Notez qu'il n'est pas pratique de copier constamment le code de contrat intelligent compilé dans le fichier de test, nous allons donc écrire un script qui écrira le code dans une constante pour nous, et nous connecterons simplement le code compilé à nos tests en utilisant "include".

Créer un fichier dans le dossier du projet build.sh avec le contenu suivant.

#!/bin/bash

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

Rendons-le exécutable.

chmod +x ./build.sh

Maintenant, il suffit d'exécuter notre script pour compiler le contrat. Mais en plus de cela, nous devons l'écrire dans une constante code. Nous allons donc créer un nouveau fichier lotter-compiled-for-test.fif, que nous inclurons dans le fichier lottery-test-suite.fif.

Ajoutons du code au script sh qui dupliquera simplement le fichier compilé dans lotter-compiled-for-test.fif et changez la dernière ligne dedans.

# 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

Maintenant, pour vérifier, exécutez le script résultant et nous allons générer un fichier lottery-compiled-for-test.fifque nous inclurons dans notre lottery-test-suite.fif

В lottery-test-suite.fif supprimer le code du contrat et ajouter la ligne "lottery-compiled-for-test.fif" include.

Exécutez des tests pour voir s'ils réussissent.

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

Super, maintenant pour automatiser le lancement des tests, créons un fichier test.sh, qui exécutera d'abord build.shpuis lancez des tests.

touch test.sh
chmod +x test.sh

Nous écrivons à l'intérieur

./build.sh 

echo "nCompilation completedn"

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

Nous faisons l' test.sh et exécutez pour vous assurer que les tests fonctionnent.

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

Nous vérifions que le contrat est compilé et que les tests sont exécutés.

Génial, maintenant au démarrage test.sh les tests seront compilés et exécutés immédiatement. Voici un lien vers commettre.

D'accord, avant de continuer, faisons encore une chose pour plus de commodité.

Créons un dossier build où nous stockerons le contrat copié et son clone écrit dans une constante lottery-compiled.fif, lottery-compiled-for-test.fif. Nous allons également créer un dossier test où sera stocké le fichier de test lottery-test-suite.fif et éventuellement d'autres fichiers de support. Lien vers les modifications associées.

Continuons à développer le contrat intelligent.

Ensuite devrait être un test qui vérifie que le message est reçu et que le compteur est mis à jour dans le magasin lorsque nous envoyons le bon numéro. Mais nous le ferons plus tard.

Réfléchissons maintenant à la structure des données et aux données qui doivent être stockées dans un contrat intelligent.

Je vais décrire tout ce que nous avons.

`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` переменная типа словарь, хранит последние двадцать ставок. 

L'étape suivante consiste à écrire deux fonctions. Appelons le premier pack_state(), qui emballera les données pour un stockage ultérieur dans le stockage de contrat intelligent. Le deuxième, nous appellerons unpack_state() lira et renverra les données du stockage.

_ 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;
}

Nous ajoutons ces deux fonctions au début du contrat intelligent. Il s'avère comme ça résultat intermédiaire.

Pour enregistrer les données, vous devrez appeler la fonction intégrée set_data() et il écrira des données à partir de pack_state() dans le stockage de contrat intelligent.

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

Maintenant que nous avons des fonctions pratiques pour écrire et lire des données, nous pouvons passer à autre chose.

Nous devons vérifier que le message entrant est signé par le propriétaire du contrat (enfin, ou un autre utilisateur ayant accès à la clé privée).

Lorsque nous publions un contrat intelligent, nous pouvons l'initialiser avec les données dont nous avons besoin dans le magasin, qui seront enregistrées pour une utilisation future. Nous y écrirons la clé publique afin de pouvoir vérifier que la signature du message entrant a bien été faite par la clé privée correspondante.

Avant de continuer, créons une clé privée et écrivons-la dans test/keys/owner.pk. Pour ce faire, démarrez Fift en mode interactif et exécutez quatre commandes.

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

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

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

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

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

Créons un dossier keys dans un dossier test et écrivez-y la clé privée.

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

On voit le fichier dans le dossier courant owner.pk.

Nous supprimons la clé publique de la pile, lorsque nous en avons besoin, nous pouvons l'obtenir de la clé privée.

Maintenant, nous devons écrire la vérification de la signature. Commençons par un test. Tout d'abord, nous lisons la clé privée du fichier à l'aide de la fonction file>B et l'écrire dans une variable owner_private_key, puis en utilisant la fonction priv>pub convertir la clé privée en public et écrire le résultat dans 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 !

Nous avons besoin des deux clés.

Nous initialisons le stockage de contrat intelligent avec des données arbitraires dans le même ordre que dans la fonction pack_state()et écrire dans une 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 !

Ensuite, nous allons composer un message signé, il ne contiendra que la signature et la valeur du compteur.

Tout d'abord, nous créons les données que nous voulons transférer, puis nous les signons avec une clé privée, et enfin nous formons un message signé.

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 !  

En conséquence, le message que nous enverrons au contrat intelligent est écrit dans la variable message_to_send, sur les fonctions hashu, ed25519_sign_uint tu peux lire dans la documentation Cinquième.

Et nous appelons à nouveau pour exécuter le test.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

comme ça le fichier de test devrait ressembler à ce stade.

Exécutons le test et il échouera, nous allons donc modifier le contrat intelligent afin qu'il puisse recevoir des messages de ce format et vérifier la signature.

Tout d'abord, nous lisons 512 bits de la signature du message et l'écrivons dans une variable, puis nous lisons 32 bits de la variable compteur.

Puisque nous avons une fonction pour lire les données du stockage de contrat intelligent, nous l'utiliserons.

Vérification supplémentaire du compteur transféré avec stockage et vérification de la signature. Si quelque chose ne correspond pas, nous lançons une exception avec le code correspondant.

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

Commit pertinent ici.

Exécutons les tests et voyons que le deuxième test échoue. Pour deux raisons, le manque de bits dans le message et le manque de bits dans le stockage, donc le code plante lors de l'analyse. Nous devons ajouter la signature du message que nous envoyons et copier le stockage du dernier test.

Dans le deuxième test, nous ajouterons une signature de message et modifierons le stockage du contrat intelligent. comme ça ressemble à un fichier avec des tests pour le moment.

Écrivons le quatrième test, dans lequel nous enverrons un message signé par la clé privée de quelqu'un d'autre. Créons une autre clé privée et enregistrons-la dans un fichier not-owner.pk. Signons le message avec cette clé privée. Exécutons les tests et assurons-nous que tous les tests réussissent. commettre à ce moment là.

Nous pouvons enfin passer à la mise en œuvre de la logique de contrat intelligent.
В recv_external() nous recevrons deux types de messages.

Étant donné que notre contrat accumulera les pertes des joueurs, cet argent doit être transféré au créateur de la loterie. L'adresse du portefeuille du créateur de la loterie est écrite dans le coffre lors de la création du contrat.

Juste au cas où, nous avons besoin de la possibilité de changer l'adresse à laquelle envoyer le gramme des perdants. Nous devrions également pouvoir envoyer des grammes de la loterie à l'adresse du propriétaire.

Commençons par le premier. Commençons par écrire un test qui vérifiera qu'après l'envoi du message, le contrat intelligent a enregistré la nouvelle adresse dans le stockage. Notez que dans le message, en plus du compteur et de la nouvelle adresse, nous envoyons également action Entier non négatif de 7 bits, en fonction de celui-ci, nous choisirons comment traiter le message dans le contrat intelligent.

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

Dans le test, vous pouvez voir comment se déroule la déréalisation du stockage des contrats intelligents storage dans Cinquième. La désérialisation des variables est décrite dans la documentation Fift.

Lien vers le commit avec test supplémentaire.

Exécutons le test et voyons s'il échoue. Ajoutons maintenant une logique pour changer l'adresse du propriétaire de la loterie.

Dans le contrat intelligent, nous continuons à analyser message, lire dans action. Rappelons que nous avons deux action: changer d'adresse et envoyer des grammes.

Ensuite, nous lisons la nouvelle adresse du titulaire du contrat et l'enregistrons dans le stockage.
Nous exécutons les tests et constatons que le troisième test échoue. Plantages dus au fait que le contrat analyse désormais en plus 7 bits du message, qui manquent dans le test. Ajoutons un message inexistant au message action. Faisons les tests et voyons que tout passe. Ici s'engager pour les changements. Super.

Écrivons maintenant la logique pour envoyer le nombre de grammes spécifié à l'adresse précédemment enregistrée.

Écrivons d'abord un test. Nous écrirons deux tests, un lorsque le solde ne suffit pas, le second lorsque tout devrait passer avec succès. Les tests peuvent être consultés dans ce commit.

Ajoutons maintenant du code. Commençons par écrire deux méthodes d'assistance. La première méthode get consiste à connaître le solde actuel du contrat intelligent.

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

Et le second sert à envoyer des grammes à un autre contrat intelligent. J'ai complètement copié cette méthode à partir d'un autre contrat intelligent.

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

Ajoutons ces deux méthodes au contrat intelligent et écrivons la logique. Tout d'abord, nous analysons le nombre de grammes du message. Ensuite, nous vérifions le solde, si ce n'est pas suffisant, nous lançons une exception. Si tout va bien, nous envoyons des grammes à l'adresse enregistrée et mettons à jour le compteur.

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

comme ça ressemble à un contrat intelligent pour le moment. Exécutons les tests et assurons-nous qu'ils réussissent.

Soit dit en passant, une commission est débitée à chaque fois pour un message traité à partir d'un contrat intelligent. Pour que les messages de contrat intelligents exécutent la demande, après des vérifications de base, vous devez appeler accept_message().

Passons maintenant aux messages internes. En effet, nous n'accepterons que les grammes et renverrons un double montant au joueur s'il gagne et un tiers au propriétaire s'il perd.

Écrivons d'abord un test simple. Pour ce faire, nous avons besoin d'une adresse de test du contrat intelligent à partir de laquelle nous envoyons des grammes au contrat intelligent.

L'adresse de contrat intelligent se compose de deux nombres, un entier 32 bits est responsable de la chaîne de travail et un numéro de compte unique entier non négatif de 256 bits dans cette chaîne de travail. Par exemple, -1 et 12345, cette adresse sera enregistrée dans un fichier.

J'ai copié la fonction d'enregistrement d'adresse de 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

Voyons comment fonctionne la fonction, cela vous donnera une idée du fonctionnement de Fift. Lancez Cinquième en mode interactif.

~/TON/build/crypto/fift -i 

On pousse d'abord -1, 12345 et le nom du futur fichier "sender.addr" sur la pile :

-1 12345 "sender.addr" 

L'étape suivante consiste à exécuter la fonction -rot, qui décale la pile de sorte que le numéro unique du contrat intelligent se trouve en haut de la pile :

"sender.addr" -1 12345

256 u>B convertit un entier non négatif de 256 bits en octets.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap échange les deux premiers éléments de la pile.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B convertit un entier 32 bits en octets.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ relie deux séquences d'octets.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Encore swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Et enfin, les octets sont écrits dans le fichier B>file. Après cela, notre pile est vide. Arrêt Fift. Fichier créé dans le dossier courant sender.addr. Déplacez le fichier dans le dossier créé test/addresses/.

Écrivons un test simple qui enverra des grammes à un contrat intelligent. Voici le commit.

Parlons maintenant de la logique de la loterie.

La première chose que nous faisons est de vérifier le message bounced ou pas si bounced, alors on l'ignore. bounced signifie que le contrat retournera des grammes si une erreur se produit. Nous ne retournerons pas les grammes si une erreur se produit, nous ne le ferons pas.

Nous vérifions si le solde est inférieur à un demi-gramme, puis nous acceptons simplement le message et l'ignorons.

Ensuite, nous analysons l'adresse du contrat intelligent d'où provient le message.

Nous lisons les données du stockage, puis supprimons les anciens paris de l'historique s'il y en a plus de vingt. Pour plus de commodité, j'ai écrit trois fonctions supplémentaires pack_order(), unpack_order(), remove_old_orders().

Ensuite, nous regardons si le solde n'est pas suffisant pour le paiement, alors nous considérons que ce n'est pas un pari, mais un réapprovisionnement et enregistrons le réapprovisionnement dans orders.

Puis enfin l'essence du contrat intelligent.

Premièrement, si le joueur a perdu, nous l'enregistrons dans l'historique des paris et si le montant est supérieur à 3 grammes, nous envoyons 1/3 au propriétaire du contrat intelligent.

Si le joueur a gagné, nous envoyons un double montant à l'adresse du joueur, puis nous enregistrons les informations sur le pari dans l'historique.

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

C'est tout. Commit pertinent.

Maintenant, cela reste simple, nous allons créer des méthodes get afin que des informations sur l'état du contrat puissent être obtenues du monde extérieur (en fait, lire les données du stockage du contrat intelligent).

Ajouter des méthodes Get. Nous écrirons ci-dessous comment recevoir des informations sur un contrat intelligent.

J'ai également oublié d'ajouter le code qui traitera la toute première requête qui se produira lors de la publication du contrat intelligent. Commit pertinent... Et plus loin fixe bug avec l'envoi de 1/3 du montant sur le compte du propriétaire.

La prochaine étape consiste à publier le contrat intelligent. Créons un dossier requests.

J'ai pris le code de publication comme base simple-wallet-code.fc который peut trouver dans le dépôt officiel.

Ce à quoi il faut prêter attention. Nous formons le stockage du contrat intelligent et le message d'entrée. Après cela, l'adresse du contrat intelligent est générée, c'est-à-dire que l'adresse est connue avant même la publication dans TON. Ensuite, vous devez envoyer quelques grammes à cette adresse, et seulement après cela, vous devez envoyer un fichier avec le contrat intelligent lui-même, car le réseau prend une commission pour stocker le contrat intelligent et les opérations qu'il contient (validateurs qui stockent et exécutent le contrat intelligent contrats). Le code peut être consulté ici.

Ensuite, nous exécutons le code de publication et obtenons lottery-query.boc adresse de fichier et de contrat intelligent.

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

N'oubliez pas de sauvegarder les fichiers générés : lottery-query.boc, lottery.addr, lottery.pk.

Entre autres choses, dans les journaux d'exécution, nous verrons l'adresse du contrat intelligent.

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

Par souci d'intérêt, nous ferons une demande à TON

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

Et nous verrons que le compte avec cette adresse est vide.

account state is empty

Nous envoyons à l'adresse 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 grammes et après quelques secondes, nous exécutons la même commande. Pour envoyer des grammes j'utilise portefeuille officiel, et vous pouvez demander des grammes de test à quelqu'un dans le chat, dont je parlerai à la fin de l'article.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Recherche un non initialisé (state:account_uninit) un contrat intelligent avec cette adresse et un solde de 1 000 000 000 nanogrammes.

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

Publions maintenant le contrat intelligent. Démarrons lite-client et exécutons.

> 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 

Vérifions que le contrat est publié.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Entre autres choses, nous obtenons

  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

On voit ça account_active.

Commit pertinent avec modifications ici.

Créons maintenant des requêtes pour interagir avec le contrat intelligent.

Plus précisément, nous laisserons le premier pour changer l'adresse en tant que travail indépendant, et nous ferons le second pour envoyer des grammes à l'adresse du propriétaire. En fait, nous devrons faire la même chose que dans le test pour envoyer des grammes.

C'est le message que nous enverrons au contrat intelligent, où msg_seqno 165 action 2 et 9.5 grammes à envoyer.

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

N'oubliez pas de signer le message avec votre clé privée lottery.pk, qui a été généré précédemment lors de la création d'un contrat intelligent. Voici le commit correspondant.

Obtenir des informations à partir d'un contrat intelligent à l'aide de méthodes get

Voyons maintenant comment exécuter des méthodes d'obtention de contrats intelligents.

Run lite-client et exécutez les méthodes get que nous avons écrites.

$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
arguments:  [ 104128 ] 
result:  [ 64633878952 ] 
...

В result contient la valeur renvoyée par la fonction balance() de notre contrat intelligent.
Nous ferons de même pour plusieurs autres méthodes.

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

Nous vous demanderons l'historique des tarifs.

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

Nous utiliserons lite-client et obtiendrons des méthodes pour afficher des informations sur le contrat intelligent sur le site Web.

Afficher les données des contrats intelligents sur le site Web

J'ai écrit un site Web Python simple pour afficher les données de contrat intelligentes de manière pratique. Ici je ne vais pas m'y attarder en détail et publier le site en un seul commit.

Les demandes à TON sont faites à partir de Python par lite-client. Pour plus de commodité, le site est packagé dans Docker et publié sur Google Cloud. Relier.

En essayant

Essayons maintenant d'y envoyer des grammes pour le réapprovisionnement à partir de portefeuille. Nous enverrons 40 grammes. Et faisons quelques paris pour plus de clarté. Nous voyons que le site affiche l'historique des paris, le pourcentage de gain actuel et d'autres informations utiles.

Nous voyonsque nous avons gagné le premier, perdu le second.

Postface

L'article s'est avéré beaucoup plus long que prévu, peut-être aurait-il pu être plus court, ou peut-être juste pour une personne qui ne connaît rien à TON et qui souhaite rédiger et publier un contrat intelligent avec lequel il n'est pas facile d'interagir. Peut-être que certaines choses pourraient être expliquées plus simplement.

Peut-être que certains points de la mise en œuvre auraient pu être réalisés avec plus d'efficacité et d'élégance, mais la préparation de l'article aurait alors pris encore plus de temps. Il est également possible que j'aie fait une erreur quelque part ou que je n'ai pas compris quelque chose, donc si vous faites quelque chose de sérieux, vous devez vous fier à la documentation officielle ou au référentiel officiel avec le code TON.

Il convient de noter que puisque TON lui-même est encore en phase de développement actif, il peut y avoir des changements qui interrompront l'une des étapes de cet article (ce qui s'est produit pendant que j'écrivais, je l'ai déjà corrigé), mais l'approche générale est peu de chances de changer.

Je ne parlerai pas de l'avenir de TON. Peut-être que la plate-forme deviendra quelque chose de grand et nous devrions prendre le temps de l'étudier et de nous tailler une place avec nos produits maintenant.

Il y a aussi Libra de Facebook, qui a un public potentiel d'utilisateurs plus large que TON. Je ne sais presque rien sur la Balance, à en juger par le forum d'activité, il y a beaucoup plus d'activité que dans la communauté TON. Bien que les développeurs et la communauté TON ressemblent davantage à un souterrain, ce qui est également cool.

références

  1. Documentation officielle sur TON : https://test.ton.org
  2. Référentiel officiel TON : https://github.com/ton-blockchain/ton
  3. Portefeuille officiel pour différentes plateformes : https://wallet.ton.org
  4. Le référentiel de contrats intelligents de cet article : https://github.com/raiym/astonished
  5. Lien vers le site Web des contrats intelligents : https://ton-lottery.appspot.com
  6. Référentiel d'extension pour Visual Studio Code for FunC : https://github.com/raiym/func-visual-studio-plugin
  7. Discutez de TON dans Telegram, ce qui a beaucoup aidé à le comprendre au stade initial. Je pense que ce ne sera pas une erreur si je dis qu'il y a tout le monde qui a écrit quelque chose pour TON. Vous pouvez également y demander des grammes de test. https://t.me/tondev_ru
  8. Une autre discussion sur TON dans laquelle j'ai trouvé des informations utiles : https://t.me/TONgramDev
  9. La première étape du concours : https://contest.com/blockchain
  10. Deuxième tour du concours : https://contest.com/blockchain-2

Source: habr.com

Ajouter un commentaire