Apie tai, kaip parašyti ir paskelbti išmaniąją sutartį „Telegram Open Network“ (TON)

Apie tai, kaip parašyti ir paskelbti išmaniąją sutartį TON

Apie ką šis straipsnis?

Straipsnyje papasakosiu apie tai, kaip dalyvavau pirmame (iš dviejų) Telegram blokų grandinės konkurse, neėmiau prizo ir nusprendžiau savo patirtį įrašyti į straipsnį, kad ji nenugrimztų į užmarštį ir, galbūt, padėtų kas nors.

Kadangi nenorėjau rašyti abstraktaus kodo, o daryti ką nors veikiančio, straipsniui parašiau išmaniąją sutartį dėl momentinės loterijos ir svetainę, kuri rodo išmaniųjų sutarčių duomenis tiesiai iš TON, nenaudojant tarpinės saugyklos.

Straipsnis bus naudingas tiems, kurie nori sudaryti savo pirmąją išmaniąją sutartį TON, bet nežino, nuo ko pradėti.

Kaip pavyzdį naudodamas loteriją, nuo aplinkos įdiegimo pereisiu prie išmaniosios sutarties paskelbimo, bendravimo su ja ir duomenų gavimo bei publikavimo svetainės rašymo.

Apie dalyvavimą konkurse

Praėjusį spalį „Telegram“ paskelbė „blockchain“ konkursą su naujomis kalbomis Fift и FunC. Reikėjo pasirinkti iš penkių siūlomų išmaniųjų sutarčių rašymo. Pagalvojau, kad būtų puiku padaryti ką nors kitaip, išmokti kalbą ir ką nors sukurti, net jei ateityje nereikės nieko daugiau rašyti. Be to, tema nuolat šmėžuoja lūpose.

Verta pasakyti, kad neturėjau patirties kuriant protingas sutartis.

Planavau dalyvauti iki pat galo, kol galėsiu, o tada parašyti apžvalginį straipsnį, bet iš karto nepavyko. aš parašė piniginę su įjungtu kelių parašu FunC ir apskritai veikė. Aš tai paėmiau kaip pagrindą išmanioji sutartis dėl solidumo.

Tuo metu maniau, kad to tikrai užteks, kad užimti bent kokią nors prizinę vietą. Dėl to apie 40 iš 60 dalyvių tapo prizininkais, o aš tarp jų nebuvau. Apskritai čia nėra nieko blogo, bet vienas dalykas mane trikdė. Rezultatų paskelbimo metu mano kontrakto testo peržiūra nebuvo atlikta, pokalbio dalyvių paklausiau, ar yra dar kas jo neturinčių, nebuvo.

Matyt, atkreipdamas dėmesį į mano žinutes, po dviejų dienų teisėjai paskelbė komentarą ir aš iki šiol nesuprantu, ar jie netyčia nepastebėjo mano išmaniojo kontrakto teisėjavimo metu, ar tiesiog pagalvojo, kad jis toks blogas, kad komentaro nereikia. Puslapyje uždaviau klausimą, bet atsakymo negavau. Nors ne paslaptis, kas teisėjavo, maniau, kad nereikia rašyti asmeninių žinučių.

Daug laiko buvo skirta supratimui, todėl buvo nuspręsta parašyti straipsnį. Kadangi informacijos dar nėra daug, šis straipsnis padės sutaupyti laiko visiems besidomintiems.

Išmaniųjų sutarčių samprata TON

Prieš ką nors rašydami, turite išsiaiškinti, iš kurios pusės kreiptis į šį dalyką. Todėl dabar aš jums pasakysiu, iš kokių dalių susideda sistema. Tiksliau, kokias dalis reikia žinoti, norint parašyti bent kokią darbo sutartį.

Daugiausia dėmesio skirsime protingos sutarties rašymui ir darbui su TON Virtual Machine (TVM), Fift и FunC, todėl straipsnis labiau panašus į įprastos programos kūrimo aprašymą. Čia nenagrinėsime, kaip veikia pati platforma.

Apskritai apie tai, kaip tai veikia TVM ir kalba Fift yra geri oficialūs dokumentai. Dalyvaudama konkurse ir dabar rašydama dabartinę sutartį dažnai kreipdavausi į ją.

Pagrindinė kalba, kuria rašomos išmaniosios sutartys FunC. Šiuo metu nėra jokios dokumentacijos, todėl norėdami ką nors parašyti, turite išstudijuoti išmaniųjų sutarčių pavyzdžius iš oficialios saugyklos ir pačios kalbos diegimą ten, taip pat galite peržiūrėti išmaniųjų sutarčių pavyzdžius iš pastarųjų dviejų konkursuose. Nuorodos straipsnio pabaigoje.

Tarkime, mes jau surašėme protingą sutartį FunC, po to sukompiliuojame kodą į Fift assembler.

Sudaryta išmanioji sutartis dar turi būti paskelbta. Norėdami tai padaryti, turite įrašyti funkciją Fift, kuri kaip įvestį paims išmaniosios sutarties kodą ir kai kuriuos kitus parametrus, o išvestis bus failas su plėtiniu .boc (tai reiškia „ląstelių maišelis“) ir, priklausomai nuo to, kaip jį rašome, privatų raktą ir adresą, kuris generuojamas pagal išmaniosios sutarties kodą. Jau dabar galite siųsti gramus dar nepaskelbtos išmaniosios sutarties adresu.

Skelbti išmaniąją sutartį TON gauta .boc failas turės būti išsiųstas į blokų grandinę naudojant lengvą klientą (daugiau apie tai žemiau). Bet prieš publikuojant reikia pervesti gramus sugeneruotu adresu, antraip išmanioji sutartis nebus publikuojama. Po paskelbimo galite bendrauti su išmaniąja sutartimi siųsdami jai pranešimus iš išorės (pavyzdžiui, naudodami lengvąjį klientą) arba iš vidaus (pavyzdžiui, viena išmanioji sutartis siunčia kitai pranešimą TON viduje).

Kai suprasime, kaip kodas skelbiamas, tampa lengviau. Apytiksliai žinome, ką norime parašyti ir kaip veiks mūsų programa. Ir rašydami ieškome, kaip tai jau įdiegta esamose išmaniosiose sutartyse, arba žiūrime į diegimo kodą Fift и FunC oficialioje saugykloje arba ieškokite oficialioje dokumentacijoje.

Labai dažnai ieškodavau raktažodžių „Telegram“ pokalbyje, kur susirinko visi konkurso dalyviai ir „Telegram“ darbuotojai, o taip atsitiko, kad konkurso metu visi ten susirinko ir pradėjo diskutuoti apie „Fift“ ir „FunC“. Nuoroda straipsnio pabaigoje.

Laikas pereiti nuo teorijos prie praktikos.

Aplinkos paruošimas darbui su TON

Aš padariau viską, kas bus aprašyta straipsnyje apie MacOS, ir dar kartą tai patikrinau švarioje Ubuntu 18.04 LTS sistemoje Docker.

Pirmas dalykas, kurį reikia padaryti, yra atsisiųsti ir įdiegti lite-client su kuria galite siųsti užklausas TON.

Oficialioje svetainėje pateiktose instrukcijose gana išsamiai ir aiškiai aprašomas diegimo procesas, o kai kurios detalės praleidžiamos. Čia mes laikomės instrukcijų, įdiegdami trūkstamas priklausomybes. Aš pats nesukompiliavau kiekvieno projekto ir įdiegiau iš oficialios Ubuntu saugyklos (naudojau 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 

Įdiegę visas priklausomybes galite įdiegti lite-client, Fift, FunC.

Pirma, mes klonuojame TON saugyklą kartu su jos priklausomybėmis. Patogumui viską darysime aplanke ~/TON.

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

Saugykloje taip pat saugomi diegimai Fift и FunC.

Dabar esame pasiruošę surinkti projektą. Saugyklos kodas klonuojamas į aplanką ~/TON/ton. Į ~/TON sukurti aplanką build ir surinkti jame projektą.

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

Kadangi ketiname rašyti išmaniąją sutartį, mums reikia ne tik lite-clientBet Fift с FunC, todėl viską sukompiliuokime. Tai nėra greitas procesas, todėl laukiame.

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

Tada atsisiųskite konfigūracijos failą, kuriame yra duomenų apie mazgą, kuriam lite-client prisijungs.

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

Pirmųjų užklausų teikimas TON

Dabar paleiskite lite-client.

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

Jei kūrimas buvo sėkmingas, po paleidimo pamatysite lengvojo kliento prisijungimo prie mazgo žurnalą.

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

Galite paleisti komandą help ir pažiūrėkite, kokios komandos galimos.

help

Išvardykime komandas, kurias naudosime šiame straipsnyje.

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

Dabar esame pasiruošę rašyti pačią sutartį.

Vykdymas

Idėja

Kaip rašiau aukščiau, išmanioji sutartis, kurią rašome, yra loterija.

Be to, tai ne loterija, kurioje reikia nusipirkti bilietą ir laukti valandą, dieną ar mėnesį, o momentinė, kurioje vartotojas perveda sutartyje nurodytu adresu. N gramų, ir akimirksniu jį atgauna 2 * N gramų arba praranda. Tikimybę laimėti padarysime apie 40%. Jei apmokėjimui neužtenka gramų, tada operaciją laikysime papildymu.

Be to, svarbu, kad statymai būtų matomi realiu laiku ir patogia forma, kad vartotojas iš karto suprastų, laimėjo ar pralaimėjo. Todėl turite sukurti svetainę, kurioje būtų rodomi statymai ir rezultatai tiesiai iš TON.

Išmanios sutarties rašymas

Patogumo dėlei paryškinau FunC kodą; įskiepį galima rasti ir įdiegti Visual Studio kodo paieškoje; jei staiga norite ką nors pridėti, įskiepį padariau viešai prieinamą. Be to, kažkas anksčiau sukūrė papildinį darbui su „Fift“, taip pat galite jį įdiegti ir rasti VSC.

Nedelsdami sukurkime saugyklą, kurioje pateiksime tarpinius rezultatus.

Kad mūsų gyvenimas būtų lengvesnis, parašysime išmaniąją sutartį ir išbandysime ją vietoje, kol ji bus paruošta. Tik po to paskelbsime TON.

Išmanioji sutartis turi du išorinius metodus, kuriuos galima pasiekti. Pirmas, recv_external() ši funkcija vykdoma, kai užklausa dėl sutarties ateina iš išorinio pasaulio, tai yra ne iš TON, pavyzdžiui, kai mes patys generuojame pranešimą ir siunčiame jį per lite-client. antra, recv_internal() Tai yra tada, kai pačioje TON bet kokia sutartis yra susijusi su mūsų. Abiem atvejais galite perduoti parametrus funkcijai.

Pradėkime nuo paprasto pavyzdžio, kuris veiks, jei bus paskelbtas, tačiau jame nėra funkcinės apkrovos.

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

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

Čia turime paaiškinti, kas tai yra slice. Visi TON Blockchain saugomi duomenys yra kolekcija TVM cell arba tiesiog cell, tokioje ląstelėje galite saugoti iki 1023 bitų duomenų ir iki 4 nuorodų į kitus langelius.

TVM cell slice arba slice tai yra dalis esamo cell naudojamas jai analizuoti, tai paaiškės vėliau. Mums svarbiausia, kad galėtume perkelti slice ir, priklausomai nuo pranešimo tipo, apdoroti duomenis recv_external() arba recv_internal().

impure — raktinis žodis, nurodantis, kad funkcija keičia išmaniosios sutarties duomenis.

Išsaugokime sutarties kodą lottery-code.fc ir sukompiliuoti.

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

Vėliavėlių reikšmę galima peržiūrėti naudojant komandą

~/TON/build/crypto/func -help

Sudarėme „Fift“ surinkėjo kodą 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

Jį galima paleisti vietoje, tam paruošime aplinką.

Atkreipkite dėmesį, kad pirmoji eilutė jungiasi Asm.fif, tai yra kodas, parašytas Fift, skirtas Fift surinkėjui.

Kadangi norime paleisti ir išbandyti išmaniąją sutartį vietoje, sukursime failą lottery-test-suite.fif ir nukopijuokite ten sukompiliuotą kodą, pakeisdami paskutinę eilutę, kuri įrašo išmaniosios sutarties kodą į konstantą codetada perkelti jį į virtualią mašiną:

"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

Kol kas atrodo aišku, dabar prie to paties failo pridėkime kodą, kurį naudosime TVM paleisti.

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 įrašome kontekstą, tai yra duomenis, su kuriais bus paleista TVM (arba tinklo būsena). Dar konkurso metu vienas iš kūrėjų parodė, kaip reikia kurti c7 ir nukopijavau. Šiame straipsnyje mums gali tekti pakeisti rand_seed kadangi nuo to priklauso atsitiktinio skaičiaus generavimas ir jei nepakeistas, kiekvieną kartą bus grąžintas tas pats skaičius.

recv_internal и recv_external konstantos su reikšmėmis 0 ir -1 bus atsakingos už atitinkamų funkcijų iškvietimą išmaniojoje sutartyje.

Dabar esame pasirengę sukurti pirmąjį tuščios išmaniosios sutarties testą. Aiškumo dėlei kol kas visus testus įtrauksime į tą patį failą lottery-test-suite.fif.

Sukurkime kintamąjį storage ir įrašykite į jį tuščią cell, tai bus išmanioji sutarties saugykla.

message Tai yra žinia, kurią perduosime išmaniajam kontaktui iš išorės. Taip pat kol kas padarysime tuščią.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Paruošę konstantas ir kintamuosius, paleidžiame TVM naudodami komandą runvmctx ir sukurtus parametrus perduoti į įvestį.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Galų gale mums pasiseks kaip šitas tarpinis kodas Fift.

Dabar galime paleisti gautą kodą.

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

Programa turėtų veikti be klaidų, o išvestyje pamatysime vykdymo žurnalą:

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

Puiku, parašėme pirmąją darbinę išmaniosios sutarties versiją.

Dabar turime pridėti funkcijų. Pirmiausia panagrinėkime žinutes, kurios ateina iš išorinio pasaulio recv_external()

Kūrėjas pats pasirenka pranešimo formatą, kurį gali priimti sutartis.

Bet paprastai

  • pirma, norime apsaugoti savo sutartį nuo išorinio pasaulio ir padaryti ją taip, kad tik sutarties savininkas galėtų jai siųsti išorines žinutes.
  • antra, kai siunčiame galiojančią žinutę TON, norime, kad tai įvyktų tiksliai vieną kartą, o kai siunčiame tą patį pranešimą dar kartą, išmanioji sutartis jį atmeta.

Taigi beveik kiekviena sutartis išsprendžia šias dvi problemas, nes mūsų sutartis priima išorinius pranešimus, todėl turime pasirūpinti ir tuo.

Mes tai padarysime atvirkštine tvarka. Pirma, išspręskime problemą su kartojimu, jei sutartis jau gavo tokį pranešimą ir jį apdorojo, antrą kartą ji nevykdys. Ir tada išspręsime problemą, kad tik tam tikras ratas žmonių galėtų siųsti žinutes į išmaniąją sutartį.

Yra įvairių būdų, kaip išspręsti pasikartojančių pranešimų problemą. Štai kaip mes tai padarysime. Išmaniojoje sutartyje gaunamų pranešimų skaitiklį inicijuojame pradine reikšme 0. Kiekviename išmaniosios sutarties pranešime pridėsime esamą skaitiklio reikšmę. Jei pranešimo skaitiklio reikšmė nesutampa su išmaniosios sutarties reikšme, mes jos neapdorojame; jei taip, tada apdorojame ir padidiname skaitiklį išmaniojoje sutartyje 1.

Grįžkime prie lottery-test-suite.fif ir pridėkite prie jo antrą testą. Jei atsiųsime neteisingą numerį, kodas turėtų pateikti išimtį. Pavyzdžiui, leiskite sutarties duomenims saugoti 166, o mes išsiųsime 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"

Paleidžiame.

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

Ir pamatysime, kad testas atliktas su klaida.

[ 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

Šiuo metu lottery-test-suite.fif turėtų atrodyti по ссылке.

Dabar prie išmaniosios sutarties pridėkime priešpriešinę logiką 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 slypi mūsų siunčiama žinia.

Pirmiausia patikriname, ar pranešime yra duomenų, jei ne, tada tiesiog išeiname.

Toliau analizuojame pranešimą. in_msg~load_uint(32) įkeliamas skaičius 165, 32 bitai unsigned int iš perduoto pranešimo.

Toliau įkeliame 32 bitus iš išmaniosios sutarties saugyklos. Patikriname, ar įkeltas skaičius sutampa su perduotu numeriu; jei ne, darome išimtį. Mūsų atveju, kadangi pravažiuojame ne rungtynes, reikėtų mesti išimtį.

Dabar sukompiliuokime.

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

Nukopijuokite gautą kodą į lottery-test-suite.fif, nepamirštant pakeisti paskutinės eilutės.

Mes patikriname, ar testas išėjo:

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

Čia Galite pamatyti atitinkamą įsipareigojimą su dabartiniais rezultatais.

Atkreipkite dėmesį, kad nepatogu nuolat kopijuoti sukompiliuotą išmaniosios sutarties kodą į failą su testais, todėl parašykime scenarijų, kuris mums kodą įrašys į konstantą, o sukompiliuotą kodą tiesiog prijungsime prie savo testų naudodami "include".

Sukurkite failą projekto aplanke build.sh su tokiu turiniu.

#!/bin/bash

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

Padarykime jį vykdomą.

chmod +x ./build.sh

Dabar tiesiog paleiskite mūsų scenarijų, kad sudarytumėte sutartį. Bet be to, turime tai įrašyti į konstantą code. Taigi mes sukursime naują failą lotter-compiled-for-test.fif, kurį įtrauksime į failą lottery-test-suite.fif.

Prie sh pridėkime skirpt kodą, kuris tiesiog nukopijuos sukompiliuotą failą lotter-compiled-for-test.fif ir pakeiskite paskutinę jos eilutę.

# 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

Dabar, norėdami patikrinti, paleiskite gautą scenarijų ir bus sugeneruotas failas lottery-compiled-for-test.fif, kurį įtrauksime į savo lottery-test-suite.fif

В lottery-test-suite.fif ištrinkite sutarties kodą ir pridėkite eilutę "lottery-compiled-for-test.fif" include.

Atliekame testus, kad patikrintume, ar jie išlaikomi.

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

Puiku, dabar, norėdami automatizuoti testų paleidimą, sukurkime failą test.sh, kuri pirmiausia bus vykdoma build.sh, tada paleiskite testus.

touch test.sh
chmod +x test.sh

Rašome viduje

./build.sh 

echo "nCompilation completedn"

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

Padarykime tai test.sh ir paleiskite jį, kad įsitikintumėte, jog testai veikia.

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

Tikriname, kad sutartis sudaryta ir testai atlikti.

Puiku, dabar paleidžiama test.sh Testai bus surinkti ir pradėti nedelsiant. Čia yra nuoroda į įsipareigoti.

Gerai, prieš tęsdami, patogumo dėlei padarykime dar vieną dalyką.

Sukurkime aplanką build kur saugosime nukopijuotą sutartį ir jos kloną, įrašytą į konstantą lottery-compiled.fif, lottery-compiled-for-test.fif. Taip pat sukurkime aplanką test kur bus saugomas bandomasis failas? lottery-test-suite.fif ir galbūt kitus pagalbinius failus. Nuoroda į atitinkamus pakeitimus.

Tęskime išmaniosios sutarties kūrimą.

Toliau turėtų būti testas, kuris patikrina, ar žinutė gauta ir skaitiklis parduotuvėje atnaujinamas, kai išsiunčiame teisingą numerį. Bet tai padarysime vėliau.

Dabar pagalvokime, kokią duomenų struktūrą ir kokius duomenis reikia saugoti išmaniojoje sutartyje.

Aprašysiu viską, ką saugome.

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

Toliau reikia parašyti dvi funkcijas. Paskambinkime pirmajam pack_state(), kuriame duomenys bus supakuoti, kad vėliau būtų išsaugoti išmaniojoje sutarties saugykloje. Paskambinkime antrajam unpack_state() skaitys ir grąžins duomenis iš saugyklos.

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

Šias dvi funkcijas pridedame prie išmaniosios sutarties pradžios. Tai pavyks kaip šitas tarpinis rezultatas.

Norėdami išsaugoti duomenis, turėsite iškviesti integruotą funkciją set_data() ir jis įrašys duomenis iš pack_state() išmaniosios sutarties saugykloje.

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

Dabar, kai turime patogias duomenų rašymo ir skaitymo funkcijas, galime judėti toliau.

Turime patikrinti, ar iš išorės gaunama žinutė yra pasirašyta sutarties savininko (arba kito vartotojo, turinčio prieigą prie privataus rakto).

Kai paskelbiame išmaniąją sutartį, galime ją inicijuoti su reikalingais duomenimis saugykloje, kurie bus išsaugoti naudoti ateityje. Ten įrašysime viešąjį raktą, kad galėtume patikrinti, ar gaunamas pranešimas buvo pasirašytas atitinkamu privačiu raktu.

Prieš tęsdami sukurkime privatųjį raktą ir įrašykite jį test/keys/owner.pk. Norėdami tai padaryti, paleiskite „Fift“ interaktyviu režimu ir vykdykime keturias komandas.

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

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

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

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

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

Sukurkime aplanką keys aplanko viduje test ir ten parašykite privatų raktą.

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

Dabartiniame aplanke matome failą owner.pk.

Mes pašaliname viešąjį raktą iš kamino ir, kai reikia, galime jį gauti iš privataus.

Dabar turime parašyti parašo patvirtinimą. Pradėkime nuo testo. Pirmiausia nuskaitome privatųjį raktą iš failo naudodami funkciją file>B ir parašykite jį į kintamąjį owner_private_key, tada naudodami funkciją priv>pub konvertuokite privatųjį raktą į viešąjį raktą ir įrašykite rezultatą 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 reikės abiejų raktų.

Išmaniąją sutarties saugyklą inicijuojame su savavališkais duomenimis ta pačia seka, kaip ir funkcijoje pack_state()ir įrašykite jį į kintamąjį 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 !

Toliau mes sudarysime pasirašytą pranešimą, kuriame bus tik parašas ir skaitiklio reikšmė.

Pirmiausia sukuriame duomenis, kuriuos norime perduoti, tada pasirašome privačiu raktu ir galiausiai sugeneruojame pasirašytą pranešimą.

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 !  

Dėl to pranešimas, kurį išsiųsime į išmaniąją sutartį, įrašomas į kintamąjį message_to_send, apie funkcijas hashu, ed25519_sign_uint galite skaityti „Fift“ dokumentacijoje.

Ir norėdami atlikti testą, skambiname dar kartą.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Čia taip Šiame etape failas su testais turėtų atrodyti taip.

Paleiskime testą ir jis nepavyks, todėl išmaniąją sutartį pakeisime taip, kad ji galėtų priimti tokio formato pranešimus ir patikrinti parašą.

Pirmiausia iš pranešimo suskaičiuojame 512 parašo bitų ir įrašome jį į kintamąjį, tada suskaičiuojame 32 skaitiklio kintamojo bitus.

Kadangi turime funkciją nuskaityti duomenis iš išmaniosios sutarties saugyklos, ją naudosime.

Toliau reikia patikrinti su saugykla perduotą skaitiklį ir patikrinti parašą. Jei kažkas nesutampa, metame išimtį su atitinkamu 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));

Atitinkamas įsipareigojimas čia.

Atlikime testus ir pamatysime, kad antrasis bandymas nepavyko. Dėl dviejų priežasčių pranešime nėra pakankamai bitų, o atmintyje nėra pakankamai bitų, todėl analizuojant kodas sugenda. Turime pridėti parašą prie siunčiamo pranešimo ir nukopijuoti saugyklą iš paskutinio bandymo.

Antrame bandyme pridėsime pranešimo parašą ir pakeisime išmaniosios sutarties saugyklą. Čia taip failas su testais atrodo kaip šiuo metu.

Parašykime ketvirtą testą, kuriame išsiųsime žinutę, pasirašytą kažkieno privačiu raktu. Sukurkime kitą privatų raktą ir išsaugokime jį faile not-owner.pk. Mes pasirašysime pranešimą šiuo privačiu raktu. Atlikime testus ir įsitikinkime, kad visi testai yra sėkmingi. Įsipareigokite šiuo momentu.

Dabar pagaliau galime pereiti prie išmaniosios sutarties logikos įgyvendinimo.
В recv_external() priimsime dviejų tipų žinutes.

Kadangi mūsų sutartyje bus kaupiami žaidėjų nuostoliai, šie pinigai turi būti pervesti loterijos sumanytojui. Sudarant sutartį saugykloje įrašomas loterijos kūrėjo piniginės adresas.

Bet kokiu atveju mums reikia galimybės pakeisti adresą, kuriuo siunčiame nevykėlių gramus. Taip pat turėtume turėti galimybę išsiųsti gramus iš loterijos savininko adresu.

Pradėkime nuo pirmojo. Pirmiausia parašykime testą, kuris patikrins, ar išsiuntus žinutę išmanioji sutartis išsaugojo naują adresą saugykloje. Atkreipkite dėmesį, kad pranešime, be skaitiklio ir naujo adreso, taip pat perduodame action 7 bitų sveikasis skaičius neneigiamas, priklausomai nuo jo pasirinksime, kaip apdoroti pranešimą išmaniojoje sutartyje.

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

Teste galite pamatyti, kaip išmaniosios sutarties saugykla yra deserializuojama storage in Fiftas. Kintamųjų deserializavimas aprašytas Fift dokumentacijoje.

Įsipareigojimo nuoroda su pridėta tešla.

Atlikime testą ir įsitikinkime, kad jis nepavyko. Dabar pridėkime logikos, kaip pakeisti loterijos savininko adresą.

Išmaniojoje sutartyje mes toliau analizuojame message, perskaitykite action. Priminsime, kad turėsime du action: pakeisti adresą ir siųsti gramus.

Tada perskaitome naują sutarties savininko adresą ir išsaugome jį saugykloje.
Atliekame testus ir matome, kad trečias bandymas nepavyko. Jis sugenda dėl to, kad dabar sutartis papildomai išanalizuoja 7 bitus iš pranešimo, kurių trūksta teste. Prie pranešimo pridėkite neegzistuojantį action. Atlikime testus ir pamatysime, ar viskas praeina. Čia pasiryžti pokyčiams. Puiku.

Dabar parašykime logiką, kaip išsiųsti nurodytą gramų skaičių anksčiau išsaugotu adresu.

Pirmiausia parašykime testą. Rašysime du testus, vieną kai neužteks pusiausvyros, antrą kai viskas turėtų sėkmingai praeiti. Testus galima peržiūrėti šiame įsipareigojime.

Dabar pridėkime kodą. Pirmiausia parašykime du pagalbinius metodus. Pirmasis gavimo metodas yra išsiaiškinti dabartinį išmaniosios sutarties likutį.

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

O antrasis skirtas gramų siuntimui į kitą išmaniąją sutartį. Visiškai nukopijavau šį metodą iš kitos išmaniosios sutarties.

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

Pridėkime šiuos du metodus prie išmaniosios sutarties ir parašykime logiką. Pirmiausia išanalizuojame pranešimo gramų skaičių. Toliau tikriname likutį, jei jo neužtenka darome išimtį. Jei viskas gerai, siunčiame gramus išsaugotu adresu ir atnaujiname skaitiklį.

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

Čia taip šiuo metu atrodo kaip protinga sutartis. Atlikime testus ir įsitikinkime, kad jie išlaikyti.

Beje, iš išmaniosios sutarties kaskart nuskaitomas komisinis mokestis už apdorotą žinutę. Kad išmaniosios sutarties pranešimai įvykdytų užklausą, atlikus pagrindinius patikrinimus reikia paskambinti accept_message().

Dabar pereikime prie vidinių pranešimų. Tiesą sakant, mes priimsime tik gramus ir grąžinsime dvigubą sumą žaidėjui, jei jis laimės, ir trečdalį savininkui, jei jis pralaimės.

Pirmiausia parašykime paprastą testą. Norėdami tai padaryti, mums reikia išmaniosios sutarties bandomojo adreso, iš kurio tariamai siunčiame gramus į išmaniąją sutartį.

Išmaniosios sutarties adresą sudaro du skaičiai: 32 bitų sveikasis skaičius, atsakingas už darbo grandinę, ir 256 bitų neneigiamas sveikasis skaičius unikalus sąskaitos numeris šioje darbo grandinėje. Pavyzdžiui, -1 ir 12345, tai yra adresas, kurį išsaugosime faile.

Nukopijavau adreso išsaugojimo funkciją iš 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

Pažiūrėkime, kaip veikia funkcija, tai leis suprasti, kaip veikia „Fift“. Paleiskite „Fift“ interaktyviu režimu.

~/TON/build/crypto/fift -i 

Pirmiausia į krūvą įstumiame -1, 12345 ir būsimo failo pavadinimą "sender.addr":

-1 12345 "sender.addr" 

Kitas žingsnis yra funkcijos vykdymas -rot, kuris perkelia krūvą taip, kad krūvos viršuje būtų unikalus išmaniosios sutarties numeris:

"sender.addr" -1 12345

256 u>B konvertuoja 256 bitų neneigiamą sveikąjį skaičių į baitus.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap sukeičia du viršutinius krūvos elementus.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B konvertuoja 32 bitų sveikąjį skaičių į baitus.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ jungia dvi baitų sekas.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Vėlgi swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Ir galiausiai baitai įrašomi į failą B>file. Po to mūsų krūva tuščia. Sustojame Fift. Dabartiniame aplanke buvo sukurtas failas sender.addr. Perkelkime failą į sukurtą aplanką test/addresses/.

Parašykime paprastą testą, kuris nusiųs gramus į išmaniąją sutartį. Štai įsipareigojimas.

Dabar pažvelkime į loterijos logiką.

Pirmas dalykas, kurį darome, yra patikrinti pranešimą bounced ar ne, jei bounced, tada mes į tai nepaisome. bounced reiškia, kad sutartis grąžins gramus, jei įvyks kokia nors klaida. Negrąžinsime gramų, jei staiga atsiras klaida.

Patikriname, jei likutis yra mažesnis nei pusė gramo, tada tiesiog priimame pranešimą ir ignoruojame.

Tada išanalizuojame išmaniosios sutarties, iš kurios buvo gautas pranešimas, adresą.

Mes nuskaitome duomenis iš saugyklos ir ištriname senus statymus iš istorijos, jei jų yra daugiau nei dvidešimt. Patogumui parašiau tris papildomas funkcijas pack_order(), unpack_order(), remove_old_orders().

Toliau žiūrime, ar likučio nepakanka mokėjimui, tada manome, kad tai ne statymas, o papildymas ir papildymą išsaugome orders.

Tada pagaliau išmaniosios sutarties esmė.

Pirma, jei žaidėjas pralaimi, išsaugome tai lažybų istorijoje ir, jei suma didesnė nei 3 gramai, 1/3 išsiunčiame išmaniosios sutarties savininkui.

Jei žaidėjas laimi, tada žaidėjo adresu išsiunčiame dvigubą sumą ir įrašome informaciją apie statymą istorijoje.

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

Štai ir viskas. Atitinkamas įsipareigojimas.

Dabar belieka tik paprasta, sukurkime gavimo metodus, kad galėtume gauti informaciją apie sutarties būklę iš išorinio pasaulio (tiesą sakant, skaityti duomenis iš jų išmaniosios sutarties saugyklos).

Pridėkime gauti metodus. Apie tai, kaip gauti informaciją apie išmaniąją sutartį, rašysime žemiau.

Taip pat pamiršau pridėti kodą, kuris apdoros pačią pirmą užklausą, atsirandančią skelbiant išmaniąją sutartį. Atitinkamas įsipareigojimas. Ir toliau pataisyta klaida siunčiant 1/3 sumos į savininko sąskaitą.

Kitas žingsnis – išmaniosios sutarties paskelbimas. Sukurkime aplanką requests.

Kaip pagrindą ėmiau publikacijos kodą simple-wallet-code.fc который gali rasti oficialioje saugykloje.

Kažkas, į ką verta atkreipti dėmesį. Sugeneruojame išmaniąją sutarties saugyklą ir įvesties pranešimą. Po to sugeneruojamas išmaniosios sutarties adresas, tai yra, adresas žinomas dar prieš paskelbiant TON. Tada šiuo adresu reikia išsiųsti kelis gramus, o tik po to reikia išsiųsti failą su pačia išmaniąja sutartimi, nes tinklas ima komisinį mokestį už išmaniosios sutarties ir operacijų joje saugojimą (validatoriai, kurie saugo ir vykdo išmaniąsias sutartis ). Kodą galite peržiūrėti čia.

Toliau vykdome publikavimo kodą ir gauname lottery-query.boc išmaniosios sutarties failas ir adresas.

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

Nepamirškite išsaugoti sugeneruotų failų: lottery-query.boc, lottery.addr, lottery.pk.

Be kita ko, vykdymo žurnaluose matysime išmaniosios sutarties adresą.

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

Kad būtų smagu, pateikime užklausą TON

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

Ir pamatysime, kad paskyra su šiuo adresu tuščia.

account state is empty

Siunčiame nurodytu adresu 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 gramus ir po kelių sekundžių vykdome tą pačią komandą. Norėdami siųsti gramus aš naudoju oficiali piniginė, ir ko nors iš pokalbio galite paprašyti bandomųjų gramų, apie kuriuos pakalbėsiu straipsnio pabaigoje.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Panašu į inicijuotą (state:account_uninit) išmanioji sutartis tuo pačiu adresu ir 1 000 000 000 nanogramų likutis.

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

Dabar paskelbkime išmaniąją sutartį. Paleiskite „Lite-client“ ir vykdykime.

> 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 

Pažiūrėkime, ar sutartis paskelbta.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Be kita ko, gauname.

  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

Mes tai matome account_active.

Atitinkamas įsipareigojimas su pakeitimais čia.

Dabar sukurkime užklausas sąveikai su išmaniąja sutartimi.

Tiksliau, pirmąjį paliksime adreso keitimui kaip savarankišką darbą, o antrąjį – gramų siuntimui savininko adresu. Tiesą sakant, mums reikės atlikti tą patį, ką ir gramų siuntimo teste.

Tai yra žinutė, kurią išsiųsime į išmaniąją sutartį, kur msg_seqno 165, action 2 ir 9.5 gramų siuntimui.

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

Nepamirškite pasirašyti pranešimo privačiu raktu lottery.pk, kuri buvo sukurta anksčiau kuriant išmaniąją sutartį. Čia yra atitinkamas įsipareigojimas.

Informacijos gavimas iš išmaniosios sutarties naudojant gavimo metodus

Dabar pažiūrėkime, kaip paleisti išmaniųjų sutarčių gavimo metodus.

Paleisti lite-client ir paleiskite gautus metodus, kuriuos parašėme.

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

В result yra reikšmė, kurią grąžina funkcija balance() iš mūsų išmaniosios sutarties.
Tą patį padarysime dar keliais būdais.

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

Paklauskime jūsų lažybų istorijos.

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

Naudosime „Lite-client“ ir gausime metodus, kaip svetainėje rodyti informaciją apie išmaniąją sutartį.

Išmaniųjų sutarčių duomenų rodymas svetainėje

Parašiau paprastą svetainę Python, kad patogiai būtų rodomi išmaniosios sutarties duomenys. Čia aš nesigilinsiu į tai išsamiai ir paskelbsiu svetainę viename įsipareigojime.

Prašymai TON pateikiami iš Python per lite-client. Patogumui svetainė yra supakuota į „Docker“ ir paskelbta „Google Cloud“. Nuoroda.

Pabandykime

Dabar pabandykime nusiųsti gramus papildymui iš piniginė. Atsiųsime 40 gramų. Ir dėl aiškumo padarykime keletą statymų. Matome, kad svetainėje rodoma statymų istorija, dabartinis laimėjimo procentas ir kita naudinga informacija.

Mes matomekad pirmą laimėjome, antrą pralaimėjome.

Afterword

Straipsnis pasirodė daug ilgesnis nei tikėjausi, gal galėjo būti trumpesnis, o gal tiesiog žmogui, kuris nieko nežino apie TON ir nori parašyti ir paskelbti ne tokią paprastą išmaniąją sutartį su galimybe bendrauti su tai. Galbūt kai kuriuos dalykus buvo galima paaiškinti paprasčiau.

Galbūt kai kuriuos įgyvendinimo aspektus būtų buvę galima atlikti efektyviau ir elegantiškiau, bet tada straipsniui parengti būtų reikėję dar daugiau laiko. Taip pat gali būti, kad aš kažkur suklydau ar kažko nesupratau, todėl jei darai ką nors rimto, reikia pasikliauti oficialia dokumentacija arba oficialia saugykla su TON kodu.

Pažymėtina, kad kadangi pats TON vis dar yra aktyvioje kūrimo stadijoje, gali atsirasti pokyčių, kurie sulaužys bet kurį šio straipsnio žingsnį (kas įvyko man rašant, jis jau buvo pataisytas), tačiau bendras požiūris yra toks. vargu ar pasikeis.

Nekalbėsiu apie TON ateitį. Galbūt platforma taps kažkuo dideliu ir turėtume skirti laiko jos studijoms ir užpildyti nišą savo produktais.

Taip pat yra Svarstyklės iš Facebook, kurios potenciali vartotojų auditorija yra didesnė nei TON. Aš beveik nieko nežinau apie Svarstykles, sprendžiant iš forumo ten daug daugiau veiklos nei TON bendruomenėje. Nors TON kūrėjai ir bendruomenė labiau primena pogrindį, o tai taip pat yra puiku.

Nuorodos

  1. Oficiali TON dokumentacija: https://test.ton.org
  2. Oficiali TON saugykla: https://github.com/ton-blockchain/ton
  3. Oficiali piniginė skirtingoms platformoms: https://wallet.ton.org
  4. Išmaniųjų sutarčių saugykla iš šio straipsnio: https://github.com/raiym/astonished
  5. Nuoroda į išmaniosios sutarties svetainę: https://ton-lottery.appspot.com
  6. „Visual Studio Code for FunC“ plėtinio saugykla: https://github.com/raiym/func-visual-studio-plugin
  7. Kalbėkitės apie TON Telegramoje, kuri tikrai padėjo tai išsiaiškinti pradiniame etape. Manau, nebus klaida, jei pasakysiu, kad ten yra visi, kurie kažką parašė TON. Ten taip pat galite paprašyti bandomųjų gramų. https://t.me/tondev_ru
  8. Kitas pokalbis apie TON, kuriame radau naudingos informacijos: https://t.me/TONgramDev
  9. Pirmasis konkurso etapas: https://contest.com/blockchain
  10. Antrasis konkurso etapas: https://contest.com/blockchain-2

Šaltinis: www.habr.com

Добавить комментарий