Par to, kā uzrakstīt un publicēt viedo līgumu Telegram Open Network (TON)

Par to, kā uzrakstīt un publicēt viedo līgumu TON

Par ko ir šis raksts?

Rakstā pastāstīšu par to, kā piedalījos pirmajā (no diviem) Telegram blokķēdes konkursā, netiku pie balvas un nolēmu savu pieredzi ierakstīt rakstā, lai tā nenogrimst aizmirstībā un, iespējams, palīdzu. kāds.

Tā kā es negribēju rakstīt abstraktu kodu, bet gan darīt kaut ko efektīvu, rakstam es uzrakstīju viedo līgumu momentloterijai un vietni, kas parāda viedā līguma datus tieši no TON, neizmantojot starpkrātuvi.

Raksts būs noderīgs tiem, kuri vēlas noslēgt savu pirmo viedo līgumu TON, bet nezina, ar ko sākt.

Izmantojot loteriju kā piemēru, es pāriešu no vides uzstādīšanas līdz viedā līguma publicēšanai, mijiedarbībai ar to un vietnes rakstīšanai datu saņemšanai un publicēšanai.

Par dalību konkursā

Pagājušā gada oktobrī Telegram izsludināja blokķēdes konkursu ar jaunām valodām Fift и FunC. Bija nepieciešams izvēlēties kādu no pieciem piedāvātajiem viedajiem līgumiem. Man šķita, ka būtu jauki darīt kaut ko savādāk, iemācīties valodu un kaut ko uztaisīt, pat ja man turpmāk nekas cits nebūtu jāraksta. Turklāt tēma nepārtraukti ir uz lūpām.

Ir vērts teikt, ka man nebija pieredzes viedo līgumu izstrādē.

Plānoju piedalīties līdz pašām beigām, kamēr varēšu un tad uzrakstīt recenzijas rakstu, bet pirmajā izgāzās uzreiz. es uzrakstīja maku ar ieslēgtu vairāku parakstu FunC un tas kopumā strādāja. Es to ņēmu par pamatu viedais līgums par Solidity.

Toreiz domāju, ka ar to noteikti pietiks, lai ieņemtu vismaz kādu godalgotu vietu. Rezultātā aptuveni 40 no 60 dalībniekiem kļuva par godalgotajiem un es viņu vidū nebiju. Kopumā ar to nav nekā slikta, bet viena lieta mani traucēja. Rezultātu paziņošanas brīdī manam līgumam testa izskatīšana nebija veikta, čata dalībniekiem jautāju, vai nav vēl kāds, kam tā nav, nebija.

Acīmredzot, pievēršot uzmanību maniem ziņojumiem, pēc divām dienām tiesneši publicēja komentāru, un es joprojām nesaprotu, vai viņi tiesāšanas laikā nejauši palaida garām manu viedo līgumu vai vienkārši domāja, ka tas ir tik slikti, ka komentārs nav vajadzīgs. Lapā uzdevu jautājumu, bet atbildi nesaņēmu. Lai gan nav noslēpums, kurš sprieda, es uzskatīju par nevajadzīgu rakstīt personiskas ziņas.

Daudz laika tika veltīts saprašanai, tāpēc tika nolemts uzrakstīt rakstu. Tā kā informācijas vēl nav daudz, šis raksts palīdzēs ietaupīt laiku ikvienam interesentam.

Viedo līgumu jēdziens TON

Pirms kaut ko rakstāt, jums ir jāizdomā, no kuras puses šai lietai pieiet. Tāpēc tagad es jums pastāstīšu, no kurām daļām sistēma sastāv. Precīzāk, kādas daļas jāzina, lai uzrakstītu vismaz kaut kādu darba līgumu.

Mēs pievērsīsimies gudra līguma rakstīšanai un darbam ar TON Virtual Machine (TVM), Fift и FunC, tāpēc raksts vairāk atgādina parastās programmas izstrādes aprakstu. Šeit mēs nekavēsimies pie tā, kā pati platforma darbojas.

Kopumā par to, kā tas darbojas TVM un valoda Fift ir laba oficiālā dokumentācija. Piedaloties konkursā un tagad rakstot kārtējo līgumu, bieži pie viņas vērsos.

Galvenā valoda, kurā tiek rakstīti viedie līgumi, ir FunC. Pašlaik par to nav dokumentācijas, tāpēc, lai kaut ko rakstītu, ir jāizpēta viedo līgumu piemēri no oficiālā repozitorija un pašas valodas ieviešana tur, kā arī varat apskatīt viedo līgumu piemērus no pēdējiem diviem sacensībām. Saites raksta beigās.

Pieņemsim, ka mēs jau esam uzrakstījuši viedo līgumu par FunC, pēc tam mēs apkopojam kodu Fift montētājā.

Sastādītais viedais līgums vēl jāpublicē. Lai to izdarītu, jums jāieraksta funkcija Fift, kas izmantos viedā līguma kodu un dažus citus parametrus kā ievadi, un izvade būs fails ar paplašinājumu .boc (kas nozīmē “šūnu maiss”) un atkarībā no tā, kā mēs to rakstām, privāto atslēgu un adresi, kas tiek ģenerēta, pamatojoties uz viedā līguma kodu. Jau tagad var sūtīt gramus uz viedā līguma adresi, kas vēl nav publicēts.

Publicēt viedo līgumu TON saņemts .boc fails būs jānosūta uz blokķēdi, izmantojot vieglo klientu (vairāk par to tālāk). Bet pirms publicēšanas ir jāpārskaita grami uz ģenerēto adresi, pretējā gadījumā viedais līgums netiks publicēts. Pēc publicēšanas varat mijiedarboties ar viedo līgumu, nosūtot tam ziņojumus no ārpuses (piemēram, izmantojot vieglo klientu) vai no iekšpuses (piemēram, viens viedais līgums nosūta citam ziņojumu TON iekšienē).

Kad mēs saprotam, kā kods tiek publicēts, tas kļūst vieglāk. Mēs aptuveni zinām, ko vēlamies rakstīt un kā mūsu programma darbosies. Un rakstot mēs meklējam, kā tas jau ir ieviests esošajos viedajos līgumos, vai arī iepazīstamies ar ieviešanas kodu Fift и FunC oficiālajā repozitorijā vai skatieties oficiālajā dokumentācijā.

Ļoti bieži meklēju atslēgas vārdus Telegram čatā, kur sapulcējās visi konkursa dalībnieki un Telegram darbinieki, un sagadījās tā, ka konkursa laikā visi tur pulcējās un sāka apspriest Fift un FunC. Saite raksta beigās.

Ir pienācis laiks pāriet no teorijas uz praksi.

Vides sagatavošana darbam ar TON

Es izdarīju visu, kas tiks aprakstīts rakstā par MacOS, un vēlreiz to pārbaudīju tīrā Ubuntu 18.04 LTS vietnē Docker.

Pirmā lieta, kas jums jādara, ir lejupielādēt un instalēt lite-client ar kuru jūs varat nosūtīt pieprasījumus uz TON.

Oficiālajā vietnē sniegtajos norādījumos ir diezgan detalizēti un skaidri aprakstīts instalēšanas process, kā arī izlaistas dažas detaļas. Šeit mēs izpildām norādījumus, pa ceļam instalējot trūkstošās atkarības. Es pats nekompilēju katru projektu un instalēju no oficiālās Ubuntu krātuves (izmantoju operētājsistēmā MacOS 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 

Kad visas atkarības ir instalētas, varat instalēt lite-client, Fift, FunC.

Pirmkārt, mēs klonējam TON repozitoriju kopā ar tā atkarībām. Ērtības labad mēs visu darīsim mapē ~/TON.

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

Repozitorijā tiek glabātas arī implementācijas Fift и FunC.

Tagad esam gatavi montēt projektu. Repozitorija kods tiek klonēts mapē ~/TON/ton. Uz ~/TON izveidot mapi build un savāc tajā projektu.

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

Tā kā mēs gatavojamies rakstīt gudru līgumu, mums ir nepieciešams ne tikai lite-clientBet Fift с FunC, tāpēc apkoposim visu. Tas nav ātrs process, tāpēc mēs gaidām.

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

Pēc tam lejupielādējiet konfigurācijas failu, kurā ir dati par mezglu, kuram lite-client savienosies.

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

Pirmo pieprasījumu iesniegšana TON

Tagad sāksim lite-client.

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

Ja būvēšana bija veiksmīga, pēc palaišanas jūs redzēsit vieglā klienta savienojuma žurnālu ar mezglu.

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

Jūs varat palaist komandu help un skatiet, kādas komandas ir pieejamas.

help

Uzskaitīsim komandas, kuras izmantosim šajā rakstā.

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

Tagad esam gatavi rakstīt pašu līgumu.

Ieviešana

Ideja

Kā jau rakstīju iepriekš, mūsu rakstītais viedais līgums ir loterija.

Turklāt šī nav loterija, kurā jāiegādājas biļete un jāgaida stunda, diena vai mēnesis, bet gan tūlītēja, kurā lietotājs pārskaitās uz līguma adresi. N gramus, un uzreiz saņem to atpakaļ 2 * N gramus vai zaudē. Uzvaras varbūtību padarīsim aptuveni 40%. Ja apmaksai nepietiek gramu, tad darījumu uzskatīsim par papildināšanu.

Turklāt ir svarīgi, lai likmes būtu redzamas reāllaikā un ērtā formā, lai lietotājs uzreiz saprastu, vai viņš uzvarēja vai zaudēja. Tāpēc jums ir jāizveido vietne, kurā tiks rādītas likmes un rezultāti tieši no TON.

Gudra līguma rakstīšana

Ērtības labad esmu izcēlis FunC kodu; spraudni var atrast un instalēt Visual Studio koda meklēšanā; ja pēkšņi vēlaties kaut ko pievienot, esmu padarījis spraudni publiski pieejamu. Arī kāds iepriekš izveidoja spraudni darbam ar Fift, to var arī instalēt un atrast VSC.

Nekavējoties izveidosim repozitoriju, kurā veiksim starprezultātus.

Lai atvieglotu mūsu dzīvi, mēs uzrakstīsim viedo līgumu un pārbaudīsim to lokāli, līdz tas būs gatavs. Tikai pēc tam mēs to publicēsim TON.

Viedajam līgumam ir divas ārējās metodes, kurām var piekļūt. Pirmkārt, recv_external() šī funkcija tiek izpildīta, ja līguma pieprasījums nāk no ārpasaules, tas ir, nevis no TON, piemēram, kad mēs paši ģenerējam ziņojumu un nosūtām to caur lite-klientu. Otrkārt, recv_internal() tas ir tad, kad pašā TON ietvaros jebkurš līgums attiecas uz mūsējo. Abos gadījumos funkcijai var nodot parametrus.

Sāksim ar vienkāršu piemēru, kas darbosies, ja tiks publicēts, taču tajā nav funkcionālas slodzes.

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

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

Šeit mums jāpaskaidro, kas tas ir slice. Visi TON Blockchain saglabātie dati ir kolekcija TVM cell vai vienkārši cell, šādā šūnā var saglabāt līdz 1023 bitiem datu un līdz 4 saitēm uz citām šūnām.

TVM cell slice vai slice šī ir daļa no esošā cell tiek izmantots tā parsēšanai, tas kļūs skaidrs vēlāk. Mums galvenais, lai varam pāriet slice un atkarībā no ziņojuma veida apstrādājiet datus recv_external() vai recv_internal().

impure — atslēgvārds, kas norāda, ka funkcija maina viedā līguma datus.

Saglabāsim līguma kodu lottery-code.fc un apkopot.

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

Karogu nozīmi var apskatīt, izmantojot komandu

~/TON/build/crypto/func -help

Mēs esam apkopojuši Fift montētāja kodu 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

To var palaist uz vietas, tam mēs sagatavosim vidi.

Ņemiet vērā, ka savienojas pirmā rinda Asm.fif, šis ir kods, kas rakstīts programmā Fift Fift montētājam.

Tā kā mēs vēlamies palaist un pārbaudīt viedo līgumu lokāli, mēs izveidosim failu lottery-test-suite.fif un nokopējiet tur apkopoto kodu, aizstājot tajā pēdējo rindiņu, kas raksta viedā līguma kodu konstantei codelai pēc tam to pārsūtītu uz virtuālo mašīnu:

"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

Pagaidām šķiet skaidrs, tagad pievienosim tam pašam failam kodu, ko izmantosim TVM palaišanai.

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 mēs ierakstām kontekstu, tas ir, datus, ar kuriem tiks palaists TVM (vai tīkla stāvoklis). Pat konkursa laikā viens no izstrādātājiem parādīja, kā izveidot c7 un es nokopēju. Šajā rakstā mums, iespējams, būs jāmaina rand_seed jo no tā ir atkarīga nejauša skaitļa ģenerēšana un, ja tas netiek mainīts, katru reizi tiks atgriezts viens un tas pats skaitlis.

recv_internal и recv_external konstantes ar vērtībām 0 un -1 būs atbildīgas par atbilstošo funkciju izsaukšanu viedajā līgumā.

Tagad mēs esam gatavi izveidot mūsu tukšā viedā līguma pirmo testu. Skaidrības labad pagaidām visus testus pievienosim vienam failam lottery-test-suite.fif.

Izveidosim mainīgo storage un ierakstiet tajā tukšu cell, šī būs viedā līguma krātuve.

message Šis ir ziņojums, ko mēs nosūtīsim viedajam kontaktam no ārpuses. Pagaidām to arī padarīsim tukšu.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Kad esam sagatavojuši konstantes un mainīgos, mēs palaižam TVM, izmantojot komandu runvmctx un nodod izveidotos parametrus ievadei.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Galu galā mums tas izdosies piemēram starpposma kods Fift.

Tagad mēs varam palaist iegūto kodu.

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

Programmai vajadzētu darboties bez kļūdām, un izvadā mēs redzēsim izpildes žurnālu:

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

Lieliski, esam uzrakstījuši viedā līguma pirmo darba versiju.

Tagad mums ir jāpievieno funkcionalitāte. Vispirms aplūkosim ziņas, kas nāk no ārpasaules recv_external()

Izstrādātājs pats izvēlas ziņojuma formātu, ko līgums var pieņemt.

Bet parasti

  • pirmkārt, mēs vēlamies aizsargāt savu līgumu no ārpasaules un padarīt to tā, lai tikai līguma īpašnieks varētu nosūtīt tam ārējus ziņojumus.
  • otrkārt, kad mēs nosūtām derīgu ziņojumu uz TON, mēs vēlamies, lai tas notiktu tieši vienu reizi, un, nosūtot to pašu ziņojumu vēlreiz, viedais līgums to noraida.

Tātad gandrīz katrs līgums atrisina šīs divas problēmas, jo mūsu līgums pieņem ārējus ziņojumus, mums arī par to ir jārūpējas.

Mēs to darīsim apgrieztā secībā. Pirmkārt, atrisināsim problēmu ar atkārtošanos, ja līgums jau ir saņēmis šādu ziņojumu un to apstrādājis, tas otrreiz to nepildīs. Un tad mēs atrisināsim problēmu, lai tikai noteikts cilvēku loks varētu nosūtīt ziņojumus viedajam līgumam.

Ir dažādi veidi, kā atrisināt problēmu ar dublētiem ziņojumiem. Lūk, kā mēs to darīsim. Viedajā līgumā mēs inicializējam saņemto ziņojumu skaitītāju ar sākotnējo vērtību 0. Katrā viedā līguma ziņojumā mēs pievienosim pašreizējo skaitītāja vērtību. Ja ziņojuma skaitītāja vērtība nesakrīt ar viedā līguma vērtību, mēs to neapstrādājam; ja tā, tad apstrādājam to un palielinām viedā līguma skaitītāju par 1.

Atgriezīsimies pie lottery-test-suite.fif un pievienojiet tam otru testu. Ja mēs nosūtām nepareizu numuru, kodam vajadzētu būt izņēmumam. Piemēram, ļaujiet līguma datiem saglabāt 166, un mēs nosūtīsim 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"

Sāksim palaist.

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

Un mēs redzēsim, ka pārbaude tiek izpildīta ar kļūdu.

[ 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

Šajā posmā lottery-test-suite.fif vajadzētu izskatīties по ссылке.

Tagad pievienosim viedajam līgumam pretloģiku 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 slēpjas mūsu sūtītais vēstījums.

Pirmā lieta, ko mēs darām, ir pārbaudīt, vai ziņojumā ir dati, ja nē, tad mēs vienkārši izejam.

Tālāk mēs parsējam ziņojumu. in_msg~load_uint(32) ielādē numuru 165, 32 bitu unsigned int no pārsūtītā ziņojuma.

Tālāk mēs ielādējam 32 bitus no viedā līguma krātuves. Mēs pārbaudām, vai ielādētais numurs atbilst nodotajam; ja nē, mēs izdarām izņēmumu. Mūsu gadījumā, tā kā izturam ne-spēli, jāmet izņēmums.

Tagad apkoposim.

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

Kopējiet iegūto kodu uz lottery-test-suite.fif, neaizmirstot aizstāt pēdējo rindiņu.

Mēs pārbaudām, vai tests ir nokārtots:

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

Tieši šeit Jūs varat redzēt atbilstošo apņemšanos ar pašreizējiem rezultātiem.

Ņemiet vērā, ka ir neērti pastāvīgi kopēt apkopoto viedā līguma kodu failā ar testiem, tāpēc mēs uzrakstīsim skriptu, kas mums ierakstīs kodu konstantē, un mēs vienkārši savienosim apkopoto kodu ar mūsu testiem, izmantojot "include".

Izveidojiet failu projekta mapē build.sh ar šādu saturu.

#!/bin/bash

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

Padarīsim to izpildāmu.

chmod +x ./build.sh

Tagad vienkārši palaidiet mūsu skriptu, lai apkopotu līgumu. Bet papildus tam mums tas jāieraksta konstantē code. Tātad mēs izveidosim jaunu failu lotter-compiled-for-test.fif, ko iekļausim failā lottery-test-suite.fif.

Pievienosim shkirpt kodu, kas vienkārši dublēs apkopoto failu lotter-compiled-for-test.fif un mainīt tajā pēdējo rindiņu.

# 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

Tagad, lai pārbaudītu, palaidīsim iegūto skriptu, un tiks ģenerēts fails lottery-compiled-for-test.fif, ko iekļausim mūsu lottery-test-suite.fif

В lottery-test-suite.fif izdzēsiet līguma kodu un pievienojiet rindu "lottery-compiled-for-test.fif" include.

Mēs veicam testus, lai pārbaudītu, vai tie ir izturējuši.

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

Lieliski! Tagad, lai automatizētu testu palaišanu, izveidosim failu test.sh, kas vispirms tiks izpildīts build.shun pēc tam palaidiet testus.

touch test.sh
chmod +x test.sh

Mēs rakstām iekšā

./build.sh 

echo "nCompilation completedn"

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

Darīsim to test.sh un palaidiet to, lai pārliecinātos, ka testi darbojas.

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

Mēs pārbaudām, vai līgums tiek sastādīts un testi tiek izpildīti.

Lieliski, tagad tiek palaists test.sh Testi tiks apkopoti un palaisti nekavējoties. Šeit ir saite uz apņemties.

Labi, pirms turpinām, ērtības labad darīsim vēl vienu darbību.

Izveidosim mapi build kur mēs glabāsim nokopēto līgumu un tā klonu, kas ierakstīts konstantē lottery-compiled.fif, lottery-compiled-for-test.fif. Izveidosim arī mapi test kur tiks saglabāts testa fails? lottery-test-suite.fif un, iespējams, citus atbalsta failus. Saite uz attiecīgajām izmaiņām.

Turpināsim attīstīt viedo līgumu.

Tālāk vajadzētu veikt testu, kas pārbauda, ​​vai ziņojums ir saņemts un skaitītājs tiek atjaunināts veikalā, kad mēs nosūtām pareizo numuru. Bet mēs to darīsim vēlāk.

Tagad padomāsim par to, kāda datu struktūra un kādi dati ir jāuzglabā viedajā līgumā.

Es aprakstīšu visu, ko mēs uzglabājam.

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

Tālāk jums jāraksta divas funkcijas. Sauksim pirmo pack_state(), kas iesaiņos datus turpmākai saglabāšanai viedā līguma krātuvē. Sauksim otro unpack_state() nolasīs un atgriezīs datus no krātuves.

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

Mēs pievienojam šīs divas funkcijas viedā līguma sākumam. Tas izdosies piemēram starprezultāts.

Lai saglabātu datus, jums būs jāizsauc iebūvētā funkcija set_data() un tas ierakstīs datus no pack_state() viedā līguma krātuvē.

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

Tagad, kad mums ir ērtas funkcijas datu rakstīšanai un lasīšanai, mēs varam turpināt.

Mums ir jāpārbauda, ​​vai no ārpuses ienākošo ziņojumu ir parakstījis līguma īpašnieks (vai cits lietotājs, kuram ir piekļuve privātajai atslēgai).

Kad mēs publicējam viedo līgumu, mēs varam to inicializēt ar mums nepieciešamajiem datiem krātuvē, kas tiks saglabāti izmantošanai nākotnē. Mēs tur ierakstīsim publisko atslēgu, lai mēs varētu pārbaudīt, vai ienākošais ziņojums ir parakstīts ar atbilstošo privāto atslēgu.

Pirms turpināt, izveidosim privāto atslēgu un ierakstīsim to test/keys/owner.pk. Lai to izdarītu, palaidīsim Fift interaktīvajā režīmā un izpildīsim četras komandas.

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

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

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

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

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

Izveidosim mapi keys mapes iekšpusē test un ierakstiet tur privāto atslēgu.

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

Mēs redzam failu pašreizējā mapē owner.pk.

Mēs noņemam publisko atslēgu no steka un, ja nepieciešams, varam iegūt to no privātās.

Tagad mums ir jāraksta paraksta pārbaude. Sāksim ar testu. Vispirms mēs nolasām privāto atslēgu no faila, izmantojot funkciju file>B un ierakstiet to mainīgajā owner_private_key, pēc tam izmantojot funkciju priv>pub konvertējiet privāto atslēgu par publisko atslēgu un ierakstiet rezultātu 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 !

Mums būs vajadzīgas abas atslēgas.

Mēs inicializējam viedā līguma krātuvi ar patvaļīgiem datiem tādā pašā secībā kā funkcijā pack_state()un ierakstiet to mainīgajā 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 !

Tālāk mēs sastādīsim parakstītu ziņojumu, tajā būs tikai paraksts un skaitītāja vērtība.

Pirmkārt, mēs izveidojam datus, kurus vēlamies pārsūtīt, pēc tam parakstām tos ar privāto atslēgu un visbeidzot ģenerējam parakstītu ziņojumu.

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 !  

Rezultātā ziņojums, ko mēs nosūtīsim viedajam līgumam, tiek ierakstīts mainīgajā message_to_send, par funkcijām hashu, ed25519_sign_uint tu vari lasīt Fift dokumentācijā.

Un, lai palaistu testu, mēs zvanām vēlreiz.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Kā šis Šajā posmā failam ar testiem vajadzētu izskatīties šādi.

Izpildīsim testu, un tas neizdosies, tāpēc mēs mainīsim viedo līgumu, lai tas varētu saņemt šāda formāta ziņojumus un pārbaudīt parakstu.

Pirmkārt, mēs saskaitām 512 bitus no ziņojuma paraksta un ierakstām to mainīgajā, pēc tam saskaitām 32 bitus no skaitītāja mainīgā.

Tā kā mums ir funkcija datu nolasīšanai no viedā līguma krātuves, mēs to izmantosim.

Tālāk tiek pārbaudīts skaitītājs, kas pārsūtīts kopā ar krātuvi un pārbaudīts paraksts. Ja kaut kas nesakrīt, tad izmetam izņēmumu ar atbilstošo kodu.

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

Attiecīgā apņemšanās šeit.

Izpildīsim testus un redzēsim, ka otrais tests neizdodas. Divu iemeslu dēļ ziņojumā nav pietiekami daudz bitu un krātuvē nav pietiekami daudz bitu, tāpēc kods avarē parsēšanas laikā. Mums ir jāpievieno paraksts ziņojumam, ko nosūtām, un jākopē krātuve no pēdējās pārbaudes.

Otrajā testā mēs pievienosim ziņojuma parakstu un mainīsim viedo līguma krātuvi. Kā šis fails ar testiem izskatās šobrīd.

Rakstīsim ceturto testu, kurā nosūtīsim ziņu, kas parakstīta ar kāda cita privāto atslēgu. Izveidosim vēl vienu privāto atslēgu un saglabāsim to failā not-owner.pk. Mēs parakstīsim ziņojumu ar šo privāto atslēgu. Izpildīsim testus un pārliecināsimies, ka visi testi ir izturējuši. Apņemties šobrīd.

Tagad mēs beidzot varam pāriet uz viedo līgumu loģikas ieviešanu.
В recv_external() mēs pieņemsim divu veidu ziņas.

Tā kā mūsu līgumā tiks uzkrāti spēlētāju zaudējumi, tad šī nauda jāpārskaita loterijas veidotājam. Loterijas veidotāja maka adrese tiek ierakstīta krātuvē, kad tiek noslēgts līgums.

Katram gadījumam mums ir nepieciešama iespēja mainīt adresi, uz kuru mēs sūtām zaudētāju gramus. Mums arī vajadzētu būt iespējai nosūtīt gramus no loterijas uz īpašnieka adresi.

Sāksim ar pirmo. Vispirms uzrakstīsim testu, kas pārbaudīs, vai pēc ziņojuma nosūtīšanas viedais līgums ir saglabājis jauno adresi krātuvē. Lūdzu, ņemiet vērā, ka ziņojumā papildus skaitītājam un jaunajai adresei mēs arī pārsūtām action 7 bitu vesels skaitlis, kas nav negatīvs, atkarībā no tā mēs izvēlēsimies, kā apstrādāt ziņojumu viedajā līgumā.

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

Testā var redzēt, kā viedlīguma krātuve tiek deserializēta storage in Fift. Mainīgo deserializācija ir aprakstīta Fift dokumentācijā.

Apstiprināt saiti ar pievienotu mīklu.

Izpildīsim testu un pārliecināsimies, ka tas neizdodas. Tagad pievienosim loģiku, lai mainītu loterijas īpašnieka adresi.

Viedajā līgumā mēs turpinām analizēt message, izlasi action. Atgādināsim, ka mums būs divi action: mainiet adresi un nosūtiet gramus.

Tad mēs nolasām jauno līguma īpašnieka adresi un saglabājam to glabāšanā.
Mēs izpildām testus un redzam, ka trešā pārbaude neizdodas. Tas avarē tāpēc, ka līgums tagad papildus parsē 7 bitus no ziņojuma, kas trūkst testā. Pievienojiet ziņojumam neesošu action. Izpildīsim testus un redzēsim, ka viss iziet. Šeit apņemties veikt izmaiņas. Lieliski.

Tagad uzrakstīsim loģiku norādītā gramu skaita nosūtīšanai uz iepriekš saglabāto adresi.

Vispirms uzrakstīsim testu. Mēs rakstīsim divus kontroldarbus, vienu, kad nebūs pietiekami daudz līdzsvara, otru, kad visam vajadzētu veiksmīgi nokārtot. Testus var apskatīties šajā saistībā.

Tagad pievienosim kodu. Vispirms uzrakstīsim divas palīgmetodes. Pirmā iegūšanas metode ir noskaidrot viedā līguma pašreizējo bilanci.

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

Un otrs ir paredzēts gramu nosūtīšanai uz citu viedo līgumu. Es pilnībā nokopēju šo metodi no cita viedā līguma.

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

Pievienosim šīs divas metodes viedajam līgumam un uzrakstīsim loģiku. Pirmkārt, mēs parsējam ziņojuma gramu skaitu. Tālāk pārbaudām bilanci, ja ar to nepietiek, metam izņēmumu. Ja viss kārtībā, tad nosūtām gramus uz saglabāto adresi un atjaunojam skaitītāju.

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

Kā šis izskatās pēc viedā līguma šobrīd. Izpildīsim testus un pārliecināsimies, ka tie ir izturējuši.

Starp citu, katru reizi par apstrādātu ziņojumu no viedā līguma tiek ieturēta komisijas maksa. Lai viedā līguma ziņojumi izpildītu pieprasījumu, pēc pamata pārbaudēm ir nepieciešams piezvanīt accept_message().

Tagad pāriesim pie iekšējiem ziņojumiem. Faktiski mēs pieņemsim tikai gramus un atsūtīsim spēlētājam dubultu summu, ja viņš uzvar, un trešo daļu īpašniekam, ja viņš zaudēs.

Vispirms uzrakstīsim vienkāršu testu. Lai to izdarītu, mums ir nepieciešama viedā līguma pārbaudes adrese, no kuras mēs it kā sūtām gramus viedajam līgumam.

Viedā līguma adrese sastāv no diviem cipariem, 32 bitu vesela skaitļa, kas atbild par darbķēdi, un 256 bitu nenegatīva vesela skaitļa unikāla konta numura šajā darbķēdē. Piemēram, -1 un 12345, šī ir adrese, kuru mēs saglabāsim failā.

Es nokopēju adreses saglabāšanas funkciju no 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

Apskatīsim, kā funkcija darbojas, tas sniegs izpratni par Fift darbību. Palaidiet programmu Fift interaktīvajā režīmā.

~/TON/build/crypto/fift -i 

Vispirms stekā ievietojam -1, 12345 un topošā faila nosaukumu "sender.addr":

-1 12345 "sender.addr" 

Nākamais solis ir funkcijas izpilde -rot, kas pārvieto steku tā, ka steka augšpusē ir unikāls viedā līguma numurs:

"sender.addr" -1 12345

256 u>B pārvērš 256 bitu nenegatīvu veselu skaitli baitos.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap apmaina divus augšējos kaudzes elementus.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B pārvērš 32 bitu veselu skaitli baitos.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ savieno divas baitu secības.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Atkal swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Un visbeidzot baiti tiek ierakstīti failā B>file. Pēc tam mūsu kaudze ir tukša. Mēs apstājamies Fift. Pašreizējā mapē ir izveidots fails sender.addr. Pārvietosim failu uz izveidoto mapi test/addresses/.

Uzrakstīsim vienkāršu testu, kas nosūtīs gramus viedajam līgumam. Lūk, apņemšanās.

Tagad aplūkosim loterijas loģiku.

Pirmā lieta, ko mēs darām, ir pārbaudīt ziņojumu bounced vai ne, ja bounced, tad mēs to ignorējam. bounced nozīmē, ka līgums atgriezīs gramus, ja radīsies kāda kļūda. Mēs neatgriezīsim gramus, ja pēkšņi radīsies kļūda.

Pārbaudām, ja atlikums ir mazāks par pusgramu, tad ziņu vienkārši pieņemam un ignorējam.

Pēc tam mēs parsējam viedā līguma adresi, no kuras tika saņemts ziņojums.

Mēs nolasām datus no krātuves un pēc tam izdzēšam vecās likmes no vēstures, ja to ir vairāk nekā divdesmit. Ērtības labad es uzrakstīju trīs papildu funkcijas pack_order(), unpack_order(), remove_old_orders().

Tālāk mēs skatāmies, ja atlikums nav pietiekams maksājumam, tad mēs uzskatām, ka tā nav likme, bet gan papildināšana un saglabājam papildināšanu orders.

Tad beidzot viedā līguma būtība.

Pirmkārt, ja spēlētājs zaudē, mēs to saglabājam likmju vēsturē un, ja summa ir lielāka par 3 gramiem, 1/3 nosūtām viedā līguma īpašniekam.

Ja spēlētājs uzvar, tad uz spēlētāja adresi nosūtām dubultu summu un pēc tam saglabājam informāciju par likmi vēsturē.

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

Viss. Atbilstoša apņemšanās.

Tagad atliek tikai vienkārši, izveidosim iegūšanas metodes, lai mēs varētu iegūt informāciju par līguma stāvokli no ārpasaules (patiesībā lasīt datus no viņu viedās līgumu krātuves).

Pievienosim iegūšanas metodes. Par to, kā saņemt informāciju par viedo līgumu, rakstīsim tālāk.

Es arī aizmirsu pievienot kodu, kas apstrādās pašu pirmo pieprasījumu, kas rodas, publicējot viedo līgumu. Atbilstoša apņemšanās. Un tālāk labots kļūda, nosūtot 1/3 no summas uz īpašnieka kontu.

Nākamais solis ir viedā līguma publicēšana. Izveidosim mapi requests.

Par pamatu ņēmu publikācijas kodu simple-wallet-code.fc kurš var atrast oficiālajā repozitorijā.

Kaut kas vērts pievērst uzmanību. Mēs ģenerējam viedo līguma krātuvi un ievades ziņojumu. Pēc tam tiek ģenerēta viedā līguma adrese, tas ir, adrese ir zināma pat pirms publicēšanas TON. Tālāk jums jānosūta vairāki grami uz šo adresi un tikai pēc tam jānosūta fails ar pašu viedo līgumu, jo tīkls ņem komisijas maksu par viedā līguma un operāciju glabāšanu tajā (validatori, kas glabā un izpilda viedo līgumu līgumi). Kodu var apskatīt šeit.

Tālāk mēs izpildām publicēšanas kodu un iegūstam lottery-query.boc viedā līguma fails un adrese.

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

Neaizmirstiet saglabāt ģenerētos failus: lottery-query.boc, lottery.addr, lottery.pk.

Cita starpā izpildes žurnālos redzēsim viedā līguma adresi.

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

Prieka pēc iesniegsim pieprasījumu TON

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

Un mēs redzēsim, ka konts ar šo adresi ir tukšs.

account state is empty

Nosūtām uz adresi 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 grami un pēc dažām sekundēm izpildām to pašu komandu. Lai nosūtītu gramus es izmantoju oficiālais maks, un kādam no čata var paprasīt testa gramus, par ko es runāšu raksta beigās.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Izskatās pēc neinicializēta (state:account_uninit) viedo līgumu ar tādu pašu adresi un atlikumu 1 000 000 000 nanogramu.

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

Tagad publicēsim viedo līgumu. Palaidīsim lite-klientu un izpildīsim.

> 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 

Pārbaudīsim, vai līgums ir publicēts.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Cita starpā mēs iegūstam.

  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

Mēs to redzam account_active.

Atbilstoša apņemšanās ar izmaiņām šeit.

Tagad izveidosim pieprasījumus mijiedarbībai ar viedo līgumu.

Precīzāk, pirmo atstāsim adreses maiņai kā patstāvīgu darbu, bet otro – gramu nosūtīšanai uz īpašnieka adresi. Patiesībā mums būs jādara tas pats, kas gramu nosūtīšanas testā.

Šo ziņu mēs nosūtīsim viedajam līgumam, kur msg_seqno 165, action 2 un 9.5 grami sūtīšanai.

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

Neaizmirstiet parakstīt ziņojumu ar savu privāto atslēgu lottery.pk, kas tika ģenerēts agrāk, veidojot viedo līgumu. Šeit ir atbilstošā apņemšanās.

Informācijas saņemšana no viedā līguma, izmantojot iegūšanas metodes

Tagad apskatīsim, kā palaist viedo līgumu iegūšanas metodes.

Palaist lite-client un palaidiet mūsu rakstītās iegūšanas metodes.

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

В result satur vērtību, ko funkcija atgriež balance() no mūsu viedā līguma.
Mēs darīsim to pašu ar vairākām citām metodēm.

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

Pieprasīsim jūsu likmju vēsturi.

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

Mēs izmantosim lite-client un iegūsim metodes, lai vietnē parādītu informāciju par viedo līgumu.

Viedo līgumu datu attēlošana vietnē

Es uzrakstīju vienkāršu vietni Python, lai ērtā veidā parādītu datus no viedā līguma. Šeit es par to sīkāk nekavēšos un publicēšu vietni vienā apņemšanā.

Pieprasījumi TON tiek veikti no Python via lite-client. Ērtības labad vietne ir iepakota programmā Docker un publicēta pakalpojumā Google Cloud. Saite.

Mēģina

Tagad mēģināsim nosūtīt gramus tur papildināšanai no seifs. Mēs nosūtīsim 40 gramus. Un skaidrības labad izdarīsim pāris likmes. Redzam, ka vietne parāda likmju vēsturi, pašreizējo laimestu procentu un citu noderīgu informāciju.

Mēs redzamka pirmo uzvarējām, otro zaudējām.

Pēcvārds

Raksts izrādījās daudz garāks, nekā biju gaidījis, varbūt varēja būt īsāks, vai varbūt tikai cilvēkam, kurš neko nezina par TON un vēlas uzrakstīt un publicēt ne pārāk vienkāršu viedo līgumu ar iespēju mijiedarboties ar to. Varbūt dažas lietas varēja izskaidrot vienkāršāk.

Varbūt dažus īstenošanas aspektus varēja veikt efektīvāk un elegantāk, bet tad raksta sagatavošanai būtu bijis nepieciešams vēl vairāk laika. Iespējams arī, ka es kaut kur kļūdījos vai kaut ko nesapratu, tāpēc, ja darāt ko nopietnu, jums jāpaļaujas uz oficiālo dokumentāciju vai oficiālo repozitoriju ar TON kodu.

Jāpiebilst, ka, tā kā pats TON vēl ir aktīvā izstrādes stadijā, var rasties izmaiņas, kas izjauks kādu no šajā rakstā minētajiem soļiem (kas notika, kamēr rakstīju, tas jau ir izlabots), taču vispārējā pieeja ir diez vai mainīsies.

Es nerunāšu par TON nākotni. Iespējams, platforma kļūs par kaut ko lielu, un mums vajadzētu veltīt laiku tās izpētei un aizpildīt nišu ar mūsu produktiem.

Ir arī Svari no Facebook, kura potenciālā lietotāju auditorija ir lielāka par TON. Es gandrīz neko nezinu par Svariem, spriežot pēc foruma, tur ir daudz lielāka aktivitāte nekā TON kopienā. Lai gan TON izstrādātāji un kopiena vairāk atgādina pagrīdi, kas arī ir forši.

atsauces

  1. Oficiālā TON dokumentācija: https://test.ton.org
  2. Oficiālā TON krātuve: https://github.com/ton-blockchain/ton
  3. Oficiālais maciņš dažādām platformām: https://wallet.ton.org
  4. Viedā līgumu krātuve no šī raksta: https://github.com/raiym/astonished
  5. Saite uz viedo līgumu vietni: https://ton-lottery.appspot.com
  6. Visual Studio Code for FunC paplašinājuma repozitorijs: https://github.com/raiym/func-visual-studio-plugin
  7. Tērzējiet par TON telegrammā, kas patiešām palīdzēja to izdomāt sākotnējā posmā. Es domāju, ka tā nebūs kļūda, ja teikšu, ka tur ir visi, kas kaut ko uzrakstījuši TON. Tur arī var paprasīt testa gramus. https://t.me/tondev_ru
  8. Vēl viena tērzēšana par TON, kurā atradu noderīgu informāciju: https://t.me/TONgramDev
  9. Sacensību pirmais posms: https://contest.com/blockchain
  10. Sacensību otrais posms: https://contest.com/blockchain-2

Avots: www.habr.com

Pievieno komentāru