Rreth mënyrës se si të shkruani dhe publikoni një kontratë inteligjente në Rrjetin e Hapët të Telegramit (TON)

Siht fjalë për të shkruar dhe publikuar një smart kontratë në TON

Për çfarë është ky artikull?

Në këtë artikull do të flas për pjesëmarrjen time në konkursin e parë (nga dy) të Telegram për blockchain, ku nuk arrita të fitoj një çmim dhe vendosa të dokumentoj përvojën në një artikull, që të mos humbiste dhe ndoshta të ndihmojë dikë.

Duke qenë se nuk dëshiroja të shkruaja kod abstrakt, por të realizoja diçka funksionale, për artikullin shkrova një smart kontratë për një lotari momentale dhe një faqe që tregon të dhënat e smart kontratës drejtpërdrejt nga TON pa përdorur ruajtje ndërmjetëse.

Artikulli do të jetë i dobishëm për ata që dëshirojnë të krijojnë smart kontratën e tyre të parë në TON, por nuk dinë nga të fillojnë.

Përmes shembullit të lotarisë, do të ecim nga instalimi i mjedisit deri te publikimi i smart kontratës, ndërveprimi me të dhe do të shkruaj një faqe për marrjen dhe publikimin e të dhënave.

Për pjesëmarrjen në konkurs

Në tetor të vitit të kaluar, Telegram shpalli një konkurs për blockchain me gjuhë të reja. Fift dhe FunC. Duhej zgjedhur një nga pesë smart kontratat e propozuara. Unë mendoja se do të ishte interesante të merja me diçka të pazakontë, të studjoja gjuhën dhe të bëja diçka, edhe nëse në të ardhmen nuk do të duhet të shkruaja më shumë. Plus, tema ishte gjithmonë në qendër të vëmendjes.

Duhet thënë se nuk kisha përvojë në zhvillimin e smart kontratave.

Planifikoja të merrja pjesë deri në fund, sa të mundja dhe pastaj të shkruaja një artikull përmbledhës, por dështova menjëherë në të parën. Unë shkrova një portofol me shumë nënshkrime në FunC dhe në përgjithësi funksionoi. E mora si model smart kontratën në Solidity..

Në atë kohë mendoja se kjo ishte mjaft për të zënë ndonjë vend në garë. Në fund, rreth 40 nga 60 pjesëmarrës fituan çmime dhe unë nuk isha midis tyre. Në përgjithësi, nuk ka asgjë të keqe me këtë, por një gjë më shqetësoi. Në momentin e shpalljes së rezultateve, nuk kishte bërë rishikim me testin për smart kontratën time, pyeta në bisedë nëse kishte ndonjë tjetër pa të, por nuk kishte.

Duket se pas disa ditĂ«sh, duke vĂ«nĂ« re mesazhet e mia, gjyqtarĂ«t publikuan njĂ« koment dhe nuk kuptova asnjĂ«herĂ« nĂ«se e kishin lĂ«nĂ« rastĂ«sisht smart kontratĂ«n time gjatĂ« gjykimit, apo thjesht e kishin konsideruar aq tĂ« dobĂ«t sa nuk kishte nevojĂ« pĂ«r koment. I kam bĂ«rĂ« pyetje nĂ« faqen, por nuk kam marrĂ« pĂ«rgjigje. Edhe pse kush e gjykoi — nuk Ă«shtĂ« sekret, e konsiderova tepricĂ« tĂ« dĂ«rgoja mesazhe private.

Ishte shpenzuar shumë kohë për ta kuptuar, prandaj vendosa të shkruaja një artikull. Duke qenë se informatat për momentin nuk janë shumë, artikulli do të ndihmojë të kursejë kohë për të gjithë të interesuarit.

Koncepti i punës së smart kontratave në TON

Para se të shkruajmë diçka, duhet të kuptojmë se si t'i qasemi kësaj gjëje. Prandaj tani do të flas për pjesët nga të cilat përbëhet sistemi. Saktësisht, çfarë pjesësh duhet të dimë për të shkruar ndonjë smart kontratë funksionale.

Ne do të përqendrohemi në shkruarjen e smart kontratës dhe punën me TON Virtual Machine (TVM), Fift dhe FunC, prandaj artikulli është më shumë si një përshkrim i zhvillimit të një programi të zakonshëm. Nuk do të ndalemi te mënyra se si funksionon platforma vetë.

Për sa i përket mënyrës se si funksionon TVM dhe gjuha Fift ka dokumentacion të mirë zyrtar. Gjatë pjesëmarrjes në konkurs dhe tani gjatë shkruajtjes së smart kontratës aktuale kam referuar shpesh në të.

Gjuha kryesore e cila përdoret për të shkruar smart kontrata është FunC. Në këtë moment, dokumentacioni për të nuk ekziston, prandaj për të shkruar diçka, duhet të studiojmë shembujt e smart kontratave nga depoja zyrtare dhe realizimin e vetë gjuhës atje, plus mund të shohim shembujt e smart kontratave nga dy konkurset e kaluara. Lidhjet janë në fund të artikullit.

Le të themi se tashmë kemi shkruar smart kontratën në FunC, tani ne e kompilojmë kodin në assembler-in Fift.

Smart kontrata e kompiluar mbetet për t'u publikuar. Për këtë, duhet të shkruajmë një funksion në Fift, i cili do të marrë si input kodin e smart kontratës dhe disa parametra të tjerë, dhe rezultati do të jetë një skedar me zgjerimin .boc çfarë do të thotë "bag of cells", dhe, në varësi të mënyrës se si do ta shkruajmë, kyçin privat dhe adresën, e cila gjenerohet mbi bazën e kodit të smart kontratës. Në adresën e smart kontratës, e cila ende nuk është publikuar, mund gjithashtu të dërgoni grama.

Për të publikuar smart kontratën në TON, skedari i marrë duhet të dërgohet në blockchain me ndihmën e një klienti të lehtë (për të cilin do të flasim më poshtë). Por para se të publikoni, duhet të transferoni gramë në adresën e gjeneruar, përndryshe smart kontrata nuk do të publikohet. Pas publikimit, do të jetë e mundur të ndërveprojmë me smart kontratën, duke i dërguar mesazhe nga jashtë (p.sh., me ndihmën e klientit të lehtë) ose nga brenda (p.sh., një smart kontratë dërgon një mesazh në një tjetër brenda TON). .boc skedha duhet të dërgohet në blockchain me ndihmën e klientit të lehtë (për këtë do flasim më poshtë). Por para se ta publikoni, duhet të transferoni gramët në adresën e gjeneruar, nd otherwise smart contract nuk do të publikohet. Pas publikimit, do të mund të ndërveproni me smart contract-in, duke i dërguar atij mesazhe nga jashtë (p.sh. me ndihmën e klientit të lehtë) ose nga brenda (p.sh. një smart contract dërgon një mesazh tjetër brenda TON).

Pasi që kuptuam si publikohet kodi, më pas bëhet më e lehtë. Ne dimë përafërsisht çfarë duam të shkruajmë dhe si do të funksionojë programi ynë. Gjatë shkrimit, ne kërkojmë si është realizuar tashmë në kontratat ekzistuese të mençura, ose shohim në kodin e realizimit. Fift dhe FunC në depozitat zyrtare, ose shikojmë në dokumentacionin zyrtar.

Shumë shpesh kërkoja me fjalë kyçe në bisedën e Telegramit ku ishin të gjithë pjesëmarrësit e garës dhe punonjësit e Telegramit gjithashtu; ndodhi që gjatë garës të gjithë u mblodhën pikërisht aty dhe filluan të diskutojnë për Fift dhe FunC. Lidhja është në fund të artikullit.

ËshtĂ« koha tĂ« kalojmĂ« nga teoria nĂ« praktikĂ«.

Përgatitja e mjedisit për punën me TON

Gjithçka që do të përshkruhet në artikull është bërë në MacOS dhe është verifikuar në një Ubuntu të pastër 18.04 LTS në Docker.

E para që duhet të bëni është të shkarkoni dhe instaloni lite-client me ndihmën e të cilit mund të dërgoni kërkesa në TON.

Udhëzimi në faqen zyrtare përshkruan procesin e instalimit mjaft në detaje dhe e kalon disa detaje. Këtu ne ndiqnim udhëzimin duke instaluar gjithashtu varësitë që mungojnë. Nuk e kompilova vetë çdo projekt dhe instalova nga depozita zyrtare e Ubuntu (në MacOS përdora 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 

Pasi të gjitha varësitë janë instaluar, mund të instaloni lite-client, Fift, FunC.

Më pas klonojmë depozitën TON së bashku me varësitë. Për lehtësi, do të bëjmë gjithçka në dosjen ~/TON.

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

Në depozitë gjithashtu ruhen realizimet. Fift dhe FunC.

Tani jemi të gatshëm të ndërtojmë projektin. Kodi i depozitës është klonuar në dosjen ~/TON/ton. Në ~/TON krijojmë dosjen ndërto dhe e ndërtojmë projektin aty.

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

Tani, pasi do të shkruajmë një kontratë të mençur, na nevojitet jo vetëm lite-client, por edhe Fift me FunC, prandaj e kompilohemi gjithashtu. Një proces i ngadalshëm, prandaj presim.

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

Më pas shkarkojmë skedarin e konfigurimit ku ndodhen të dhënat për nodin me të cilin lite-client do të lidhemi.

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

Bëjmë kërkesat e para në TON

Tani do të fillojmë lite-client.

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

Nëse ndërtimi ka kaluar me sukses, atëherë pas nisin do të shihni logun e lidhjes së klientit të lehtë me nodin.

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

Mund të ekzekutoni komandën help dhe të shihni cilat komanda janë të disponueshme.

help

Le të rendisim komandat që do të përdorim në këtë artikull.

list of available commands:
last    Get last block and state info from server
sendfile  Load a serialized message from  and send it to server
getaccount  [&]  Loads the most recent state of specified account;  is in [&:] format
runmethod  [&]  ...   Runs GET method  of account  with specified parameters

last merr bllokun më të fundit të krijuar nga serveri.

sendfile  dërgon një skedar me mesazh në TON; me këtë komandë publikohet kontrata e mençur dhe kërkesat ndaj saj.

getaccount  ngarkon gjendjen aktuale të kontratës së mençur me adresën e specifikuar.

runmethod  [&]    ekzekuton metodat get të kontratës së mençur. 

Tani jemi të gatshëm për të shkruar vetë kontratën.

Realizimi

Ideja

Siç e përmenda më sipër, kontrata e mençur që po shkruajmë është një lotari.

Dhe kjo nuk është një lotari ku duhet të blini një biletë dhe të prisni një orë, ditë ose muaj, por një momentale ku përdoruesi transferon gramë në adresën e kontratës N dhe menjëherë merr prapa 2 * N gramë ose humbet. Probabilitetin e fitimit do ta vendosim rreth 40%. Nëse gramët për pagesë nuk janë të mjaftueshme, do të konsiderojmë transaksionin si një depozitë.

ËshtĂ« e rĂ«ndĂ«sishme qĂ« bastet tĂ« jenĂ« tĂ« dukshme nĂ« kohĂ« reale dhe nĂ« njĂ« format tĂ« pĂ«rshtatshĂ«m, pĂ«r t'i lejuar pĂ«rdoruesit tĂ« kuptojĂ« menjĂ«herĂ« nĂ«se ka fituar apo humbur. Prandaj Ă«shtĂ« e nevojshme tĂ« krijojmĂ« njĂ« faqe interneti qĂ« do tĂ« tregojĂ« bastet dhe rezultatet direkt nga TON.

Shkrimi i kontratës së mençur

Për lehtësi, kam bërë një theksim të kodit për FunC; plugini mund të gjendet dhe të instalohet në kërkimin e Visual Studio Code, nëse dëshironi të shtoni diçka, kam publikuar plugini në qasje të hapur. Po ashtu, më parë dikush kishte bërë një plugin për punë me Fift, gjithashtu mund të gjendet dhe të instalohet në VSC.

Më parë krijojmë një depo ku do të depozitojmë rezultatet e përkohshme.

Për të lehtësuar vetën, do të shkruajmë kontratën e mençur dhe do ta testojmë lokal, deri sa ajo të jetë gati. Vetëm pasi të jetë gati, do ta publikojmë në TON.

Kontrata e mençur ka dy metoda të jashtme të cilat mund të thirren. E para, recv_external() kjo funksion ekzekutohet kur një kërkesë ndaj kontratës bëhet nga bota e jashtme, domethënë jo nga TON, për shembull kur ne formojmë mesazhin dhe e dërgojmë atë përmes lite-client. E dyta, recv_internal() Ky është kur brenda TON-it, një kontratë e caktuar i drejtohet tonës tonë. Në të dyja rastet, mund të kalojmë parametra në funksion.

Le të fillojmë me një shembull të thjeshtë që do të funksionojë nëse publikojmë, por nuk ka ngarkesë funksionale.

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

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

Këtu duhet të shpjegojmë se çfarë është slice. Të dhënat e ruajtura në Blockchain-in TON janë një koleksion TVM cell ose thjesht cell, në një qelizë të tillë mund të ruajmë deri në 1023 bit të dhënash dhe deri në 4 lidhje me qeliza të tjera.

TVM cell slice ose slice është një pjesë e ekzistueshme cell përdoret për ta analizuar, më vonë do të kuptohet. E rëndësishme për ne është se në kontratën smart mund të kalojmë slice dhe në varësi të llojit të mesazhit, të përpunojmë të dhënat në recv_external() ose recv_internal().

impure — fjalĂ« kyçe qĂ« tregon se funksioni ndryshon tĂ« dhĂ«nat e kontratĂ«s smart.

Do ta ruajmë kodin e kontratës në lottery-code.fc dhe do ta kompilohet.

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

Vlerat e flag-eve mund të shikohen me komandën

~/TON/build/crypto/func -help

Kemi marrë kodin Fift të kompiliuar në 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

Mund ta ekzekutojmë lokal, për këtë do të përgatitim ambientin.

Vërejmë, rreshti i parë lidhet Asm.fif, kjo është një kod i shkruar në Fift për assembler-in Fift.

Sepse duam të ekzekutojmë dhe të testojmë kontratën smart lokal, do të krijojmë një skedar lottery-test-suite.fif dhe do ta kopjojmë kodin e kompiliuar, duke ndërruar rreshtin e fundit, i cili ruan kodin e kontratës smart në një konstante kode, për ta kaluar më vonë në makinën virtuale:

"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

Për momentin duket e qartë, tani do të shtojmë në të njëjtin skedar kodin që do të përdorim për të ekzekutuar TVM.

0 tuple 0x076ef1ea , // magjia
0 , 0 , // veprimet msg_sent
1570998536 , // unix_time
1 , 1 , 3 , // block_lt, trans_lt, rand_seed
0 tuple 100000000000000 , dictnew , , // bilanci i mbetur
0 , dictnew , // contract_address, global_config
1 tuple // mbështjell në një tuple tjetër
constant c7

0 constant recv_internal // për të ekzekutuar recv_internal() 
-1 constant recv_external // për të thirrur recv_external()

Në c7 ne regjistrojmë kontekstin, dmth të dhënat me të cilat do të ekzekutohet TVM (ose gjendja e rrjetit). Po ashtu, gjatë konkursit një nga zhvilluesit tregoi se si krijohet c7 dhe unë kopjova. Në këtë artikull mund të duhet të ndryshojmë rand_seed sepse nga ai varet gjenerimi i numrit të rastësishëm dhe mos ta ndryshojmë, përndryshe çdo herë do të kthejë të njëjtin numër.

recv_internal dhe recv_external konstanta me vlerën 0 dhe -1 do të përgjigjen për thirrjen e funksioneve përkatëse në kontratën smart.

Tani jemi gati të krijojmë testin e parë për kontratën tonë të zbrazët smart. Për qartësi për momentin do të shtojmë të gjithë testet në këtë të njëjtin skedar lottery-test-suite.fif.

Do të krijojmë një variabël storage dhe do ta ruajmë në të njëtë cell, kjo do të jetë magazina e kontratës smart.

message ky është mesazhi që do t'i kalojmë kontratës smart nga jasht. Po ashtu, do ta bëjmë për momentin bosh.

ruajtje variablash 
<b b> ruajtje ! 

mesazh variabël 
<b b> mesazh ! 

Pasi kemi përgatitur konstantet dhe variablat, ne e ekzekutojmë TVM përmes komandës runvmctx dhe kalojmë parametrat e krijuar si hyrje.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Si përfundim do të kemi një kod të tillë ndërmjetës Fift.

Tani mund të ekzekutojmë kodin që kemi marrë.

export FIFTPATH=~/TON/ton/crypto/fift/lib // e kryejmë një herë për lehtësi 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Programi duhet të funksionojë pa gabime dhe në output do të shohim logun e ekzekutimit:

execute SETCP 0
execute DICTPUSHCONST 19 (xC_,1)
execute DICTIGETJMPZ
execute DROP
execute implicit RET
[ 3][t 0][1582281699.325381279][vm.cpp:479]     hapje: 5 gaz: përdorur=304, maksimum=9223372036854775807, kufizim=9223372036854775807, kredi=0

Shumë mirë, ne shkruam versionin e parë funksional të kontratës smart.

Tani duhet të shtojmë funksionalitete. Së pari do të lehtësojmë mesazhet që vijnë nga bota e jashtme në recv_external()

Zhvilluesi vetë zgjedh formatin e mesazhit që kontrata mund të pranojë.

Por zakonisht,

  • sĂ« pari, duam tĂ« mbrojmĂ« kontratĂ«n tonĂ« nga bota e jashtme dhe tĂ« bĂ«jmĂ« qĂ« vetĂ«m pronari i kontratĂ«s tĂ« mund tĂ« dĂ«rgojĂ« mesazhe tĂ« jashtme nĂ« tĂ«.
  • nĂ« tĂ« dytĂ«n, kur dĂ«rgojmĂ« njĂ« mesazh tĂ« vlefshĂ«m nĂ« TON, duam qĂ« kjo tĂ« ndodhĂ« pikĂ«risht njĂ« herĂ« dhe kur tĂ« dĂ«rgohet sĂ«rish tĂ« njĂ«jtin mesazh, kontrata smart ta refuzojĂ«.

Prandaj, në pothuajse çdo kontratë zgjidhen këto dy probleme, sepse kontrata jonë pranonte mesazhe nga jasht, ne gjithashtu duhet të kujdesemi për këtë.

Do ta bëjmë në rendin e kundërt. Së pari, do të zgjidhim problemin e përsëritjes, nëse kontrata tashmë e ka marrë një mesazh të tillë dhe e ka përpunuar atë, nuk do ta ekzekutojë një herë tjetër. Pastaj, do të zgjidhim problemin që vetëm një grup i caktuar mund të dërgojë mesazhe në kontratën smart.

Ka janë mënyra të ndryshme për të zgjidhur problemin me mesazhet e përsëritura. Ne do ta bëjmë kështu. Në kontratën smart, do të inicializojmë një numërues për mesazhet e pranuara me vlerën fillestare 0. Në çdo mesazh do t'i shtojmë kontratës smart vlerën aktuale të numëruesit. Nëse vlera e numëruesit në mesazh nuk përputhet me vlerën në kontratën smart, atëherë ne nuk e përpunojmë atë; nëse përputhet, atëherë e përpunojmë dhe rrisim numëruesin në kontratën smart me 1.

Kthehemi në lottery-test-suite.fif dhe shtojmë në të testin e dytë. Do të dërgojmë një numër të gabuar, kodi duhet të lançohet një përjashtim. Për shembull, le të ruhet në të dhënat e kontratës 166, ndërsa ne dërgojmë 165.

<b 166 32 u, b> ruajtje !
<b 165 32 u, b> mesazh !

mesazh @ 
recv_external 
code 
ruajtje @ 
c7 
runvmctx

drop 
exit_code ! 
."Kodo i daljes " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Nuk kaloi"

Le ta nisim.

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

Dhe do të shohim që testi ekzekutohet me një gabim.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Testi #2 nuk kaloi
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Gabim në interpretimin e skedarit `lottery-test-suite.fif`: gabim në interpretimin e skedarit të përfshirë `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Testi #2 nuk kaloi

Në këtë fazë lottery-test-suite.fif duhet të duket si në lidhje.

Tani le të shtojmë logjikën e numëruesit në kontratën smart në lottery-code.fc.

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

() recv_external(slice in_msg) impure {
    nëse (slice_empty?(in_msg)) {
        kthehu (); 
    }
    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);
}

Në slice in_msg është mesazhi që ne dërgojmë.

E para që bëjmë është të kontrollojmë nëse në mesazh ka të dhëna; nëse jo, atëherë thjesht dalim.

Më pas ne analizojmë mesazhin. in_msg~load_uint(32) ngarkon numrin 165, 32-bitësh unsigned int nga mesazhi i dërguar.

Më pas ngarkojmë 32 bita nga ruajtja e kontratës smart. Kontrollojmë që numri i ngarkuar përputhet me atë të dërguar; nëse jo, lançohet një përjashtim. Në rastin tonë, duke qenë se dërgojmë një numër të papërshtatshëm, duhet të lansohet një përjashtim.

Tani le të kompilohet.

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

Kodi i marrë le të kopjohet në lottery-test-suite.fif, pa harruar të ndryshohet rreshti përfundimtar.

Kontrolloni nëse testi kalon:

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

Ja këtu mund të shikoni komitin përkatës me rezultatet aktuale.

Vini re se është e pakëndshme të kopjoni vazhdimisht kodin e kompiliuar të kontratës smart në skedarët e testeve, prandaj do të shkruajmë një skenar që do ta regjistrojë kodin në një konstant për ne, dhe ne thjesht do të lidhim kodin e kompiliuar në testet tona me anë të "include".

Në dosjen e projektit krijojmë një skedar build.sh me përmbajtjen e mëposhtme.

#!/bin/bash

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

Le ta bëjmë atë ekzekutues.

chmod +x ./build.sh

Tani, mjafton të nisim skenarin tonë për të kompiliuar kontratën. Por përveç kësaj, duhet ta regjistrojmë atë në një konstant kode. Prandaj, do të krijojmë një skedar të ri lottery-compiled-for-test.fif, të cilin do ta përfshijmë në skedarin lottery-test-suite.fif.

Do të shtojmë në skenarin sh kodin që do ta kopjojë thjesht skedarin e kompiliuar në lottery-compiled-for-test.fif dhe do të ndryshojë rreshtin e tij të fundit.

# 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

Tani për të verifikuar, do të nisim skenarin e marrë dhe do të na gjenerohet skedari lottery-compiled-for-test.fif, të cilin do ta përfshijmë në lottery-test-suite.fif

Në lottery-test-suite.fif e heqim kodin e kontratës dhe shtojmë rreshtin "lottery-compiled-for-test.fif" include.

Nisim testet për të verifikuar që ato kalojnë.

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

Shkëlqyer, tani, për ta automatizuar ekzekutimin e testeve krijojmë një skedar test.sh, i cili fillimisht do të ekzekutojë build.sh, pastaj do të niste testet.

touch test.sh
chmod +x test.sh

Më pas brenda shkruajmë

./build.sh 

echo "nKompilimi përfundoi n"

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

Le ta bëjmë test.sh dhe ta nisim për të siguruar funksionalitetin e testeve.

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

Kontrollojmë që kontrata të kompilohet dhe testet të ekzekutohen.

Shkëlqyer, tani me nisjen test.sh do të ndodhi menjëherë kompilimi dhe ekzekutimi i testeve. Këtu është lidhja për komit.

Ok, përpara se të vazhdojmë, le të bëjmë edhe një gjë për lehtësim.

Krijojmë një dosje ndërto ku do të ruajmë kontratën e kompiliuar dhe klonin e saj të regjistruar në konstantë lottery-compiled.fif, lottery-compiled-for-test.fif. Po ashtu, do të krijojmë një dosje test ku do të ruhen skedarët e testeve lottery-test-suite.fif dhe potencialisht skedarë ndihmës të tjerë. Linku për ndryshimet përkatëse.

Vazhdojmë zhvillimin e kontratës smart.

Në vazhdim duhet të ketë një test që kontrollon se mesazhi pranohet dhe numëruesi përditësohet në ruajtje kur dërgojmë një numër të saktë. Por do ta bëjmë këtë më vonë.

Tani le të mendojmë për strukturën e të dhënave dhe cilat të dhëna nevojiten për të ruajtur në kontratën smart.

Do të përshkruaj gjithçka që ruajmë.

`seqno` një numër të plotë pozitiv 32-bitësh që shërben si numër llogaritës. 

`pubkey` një numër të plotë pozitiv 256-bitësh që është çelësi publik, me të cilin do të verifikojmë nënshkrimin e mesazheve të dërguara nga jashtë, për këtë më poshtë. 

`order_seqno` një numër të plotë pozitiv 32-bitësh që ruan llogaritësin e numrit të ofertave. 

`number_of_wins` një numër të plotë pozitiv 32-bitësh që ruan numrin e fitoreve. 

`incoming_amount` lloj të dhënash Gram (katër bitët e parë përgjigjen për gjatësi), ruan sasinë totale të gramëve që janë dërguar në kontratë. 

`outgoing_amount` sasia totale e gramëve që është dërguar fituesve. 

`owner_wc` numri i bitcoin-it të punës, një numër të plotë 32-bitësh (në disa vende thuhet se është 8-bitësh). Aktualisht ka vetëm dy -1 dhe 0. 

`owner_account_id` një numër të plotë pozitiv 256-bitësh, adresa e kontratës në bitcoin-in e punës aktual. 

`orders` një variabël e tipit fjalor që ruan tridhjetë ofertat e fundit. 

Më pas duhet të shkruajmë dy funksione. Funksoni i parë do ta quajmë pack_state(), i cili do të paketojë të dhënat për ruajtjen e saj të mëtejshme në depozitat e kontratës. Funksoni i dytë, do ta quajmë unpack_state() do të lexojë dhe kthejë të dhënat nga depozita.

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

Shtojmë këto dy funksione në fillim të kontratës. Do të rezultojë një kod të tillë një rezultat ndërmjetës.

Për të ruajtur të dhënat, duhet të thërrasim funksionin e integruar set_data() dhe ai do të shkruajë të dhënat nga pack_state() në depozitat e kontratës.

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

Tani që kemi funksione të përshtatshme për ruajtjen dhe leximin e të dhënave, mund të vazhdojmë më tutje.

Na nevojitet të verifikojmë se mesazhi i ardhshëm është nënshkruar nga pronari i kontratës (ose ndonjë përdorues tjetër që ka akses në çelësin privat).

Kur publikojmë kontratën, ne mund ta inicializojmë me të dhënat që na nevojiten në depozitë, të cilat do të ruajnë për përdorim të ardhshëm. Ne do ta shkruajmë atje çelësin publik, në mënyrë që të mund të verifikohet se nënshkrimi i mesazhit të ardhshëm është bërë me çelësin privat përkatës.

Para se të vazhdojmë, le të krijojmë një çelës privat dhe ta shkruajmë atë në test/keys/owner.pk. Për këtë, të shkojmë Fift në mënyrë interaktive dhe të ekzekutojmë katër komanda.

`newkeypair` gjeneron çelësin publik dhe privat dhe i shënon ata në STACK. 

`drop` heq elementin e sipërm nga STACK (në këtë rast çelësi publik)  

`.s` thjesht shikon çfarë është aktuamente në STACK 

`"owner.pk" B>file` shkruan çelësin privat në një skedar me emrin `owner.pk`. 

`bye` përfundon punën me Fift. 

Krijojmë një dosje keys dhe shkruajmë brenda kësaj dosjeje çelësin privat. test 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

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

E shohim në dosjen aktuale skedarin owner.pk.

Ne heqim çelësin publik nga STACK, dhe kur të nevojitet mund ta marrim atë nga çelësi privat.

Tani na nevojitet të shkruajmë verifikimin e nënshkrimit. Të fillojmë me një test. Më parë ne lexojmë çelësin privat nga skedari me funksionin file>B dhe e shënojmë atë në variablën owner_private_key, më pas me ndihmën e funksionit priv>pub konvertojmë çelësin privat në publik dhe shënojmë rezultatin në owner_public_key.

variabla owner_private_key
variabla owner_public_key 

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !

Të dy çelësat do të na nevojiten.

Inicializojmë depozitën e kontratës me të dhëna të rastësishme në të njëjtën renditje si në funksionin pack_state()dhe do ta shënojmë në variablë storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

"./keys/owner.pk" file&gt;B owner_private_key !
owner_private_key @ priv&gt;pub owner_public_key !
dictnew orders !
0 owner_wc !
0 owner_account_id !

<b 0 32 u, owner_public_key @ b, gram, owner_wc i, owner_account_id 256 orders dict, b> ruajtje !

Më pas do të përgatisim një mesazh të nënshkruar, në të do të ketë vetëm nënshkrimin dhe vlerën e llogaritësit.

Së pari krijojmë të dhënat që duam të dërgojmë, pastaj nënshkruajmë ato me çelësin privat dhe përfundimisht formojmë mesazhin e nënshkruar.

mesazh për t'u nënshkruar
mesazh për t'u dërguar
nënshkrimi
<b 0 32 u, b> mesazh për t'u nënshkruar !
mesazh për t'u nënshkruar @ çelësi_private @ ed25519_nënshkruaj_uint nënshkrimi !
<b signature @ b, 0 32 u, b> &lt;s !  

Në përfundim, mesazhi që do të dërgojmë në kontratë shkruhet në variablën message_to_send, për funksionet hashu, ed25519_sign_uint mund të lexoni në dokumentacionin për Fift.

Dhe për të ekzekutuar testin përsëri thërrasim.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Kështu duhet të duket skedari me testet në këtë fazë.

Të ekzekutojmë testin dhe ai do të dështojë, ndaj do ta ndryshojmë kontratën që të mund të pranojë mesazhe të tilla dhe të verifikojë nënshkrimin.

Fillimisht lexojmë nga mesazhi 512 bit të nënshkrimit dhe e ruajmë në variablën, më pas lexojmë 32 bit të llogaritësit.

Pasi kemi funksionin e leximit të të dhënave nga depozita e kontratës inteligjente, do ta përdorim atë.

Më pas, kontrollojmë numrin e dërguar me depozitën dhe verifikojmë nënshkrimin. Nëse diçka nuk përputhet, atëherë hedhim një përjashtim me kodin përkatës.

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

Komiti përkatës këtu.

Do të ekzekutojmë testet dhe do të shohim se testi i dytë dështoi. Për dy arsye, mungesa e bitëve në mesazh dhe mungesa e bitëve në depozitë, kështu që kodi dështon gjatë parse-imit. Duhet të shtojmë nënshkrimin e mesazhit që dërgojmë dhe të kopjojmë depozitën nga testi i fundit.

Në testin e dytë do të shtojmë nënshkrimin e mesazhit dhe do të ndryshojmë depozitën e kontratës inteligjente. Kështu Po shikojmë momentalisht skedarin me testet.

Do të shkruajmë testin e katërt, në të cilin do të dërgojmë një mesazh të nënshkruar me çelësin privat të dikujt tjetër. Do të krijojmë një çelës privat tjetër dhe do ta ruajmë në skedar not-owner.pk. Me këtë çelës privat do të nënshkruajmë mesazhin. Do të ekzekutojmë testet dhe do të sigurohemi se të gjitha testet kalojnë. Komiti në këtë moment.

Tani në fund mund të kalojmë në implementimin e logjikës së kontratës inteligjente.
Në recv_external() Ne do të pranojmë dy lloje mesazhesh.

Pasi kontrata jonë do të akumulojë humbjet e lojtarëve, këto para duhet të transferohen te krijuesi i lotarisë. Adresa e portofolit të krijuesit të lotarisë regjistrohet në depozitë gjatë krijimit të kontratës.

Për çdo rast, na nevojitet mundësia për të ndryshuar adresën për të dërguar gramët e humbësve. Po ashtu, duhet të kemi mundësinë të dërgojmë gramët nga lotaria në adresën e pronarit.

Le të fillojmë me të parën. Së pari do të shkruajmë një test që do të verifikojë se pas dërgimit të mesazhit, kontrata inteligjente e ruan adresën e re në depozitë. Të kemi parasysh se në mesazh përveç numrave dhe adresës së re, ne po dërgojmë gjithashtu veprim një numër të plotë pozitiv 7-bitësh, në varësi të tij do të zgjedhim si ta trajtojmë mesazhin në kontratën inteligjente.

<b 0 32 u, 1 @ 7 new_owner_wc i, new_owner_account_id 256 b> mesazhi për të nënshkruar !

Në test mund të shohim se si ndodh deserializimi i depozitës së kontratës inteligjente storage në Fift. Deserializimi i variablave është përshkruar në dokumentacionin për Fift.

Lidhja me komitin me shtimin e testit.

Ekuvalojmë testin dhe shohim se ai dështon. Tani do të shtojmë logjikën për të ndryshuar adresën e pronarit të lotarisë.

Në kontratën inteligjente vazhdojmë të parse-ojmë message, lexojmë në veprim. Le të rikujtojmë se do të kemi dy veprim: ndryshimin e adresës dhe dërgimin e gramëve.

Pastaj lexojmë adresën e re të pronarit të kontratës dhe e ruajmë në depozitë.
Do të ekzekutojmë testet dhe do të shohim se testi i tretë dështoi. Dështoi për shkak se kontrata tani parse-on gjithashtu 7 bitë nga mesazhi, të cilat mungojnë në test. Do të shtojmë në mesazh një veprimtë papërkufizuar. Do të ekzekutojmë testet dhe do të shohim se të gjitha kalojnë. Këtu komiti për ndryshimet. Mirë.

Tani le të shkruajmë logjikën për dërgimin e sasisë së dhënë të gramëve në adresën e ruajtur më parë.

Së pari do të shkrujmë testin. Do të shkruajmë dy teste, një kur balanca mungon, tjetra kur gjithçka duhet të kalojë me sukses. Testet mund të shihen në këtë komit.

Tani do të shkruajmë kodin. Së pari do të shkruajmë dy metoda ndihmëse. Metoda e parë është një metodë get për të ditur balancën aktuale të kontratës inteligjente.

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

Dhe e dyta për dërgimin e gramëve në një kontratë tjetër inteligjente. Këtë metodë e kam kopjuar plotësisht nga një kontratë tjetër inteligjente.

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

Do të shtojmë këto dy metoda në kontratën inteligjente dhe do të shkruajmë logjikën. Së pari do të parse-ojmë sasinë e gramëve nga mesazhi. Më pas do të kontrollojmë balancën; nëse nuk ka mjaftueshëm, do të hedhim një përjashtim. Nëse gjithçka është në rregull, atëherë dërgojmë gramët në adresën e ruajtur dhe përditësojmë numëruesin.

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ështu duket si kontrata inteligjente deri në këtë moment. Do të ekzekutojmë testet dhe do të sigurohemi se ato kalojnë.

Kështu, për çdo mesazh të përpunuar nga kontrata smart, ngarkohet një tarifë. Që kontrata smart të përmbushë kërkesën për mesazhe, pas verifikimeve të bazës, duhet të thërrasim accept_message().

Tani do të merremi me mesazhet e brendshme. Praktikisht, do të pranojmë gramët dhe do t'ua dërgojmë lojtarëve dyfishin e shumës në rast fitimi dhe një të tretën pronarit në rast humbjeje.

Së pari, do të shkruajmë një test të thjeshtë. Për këtë do të na nevojitet një adresë testimi e kontratës smart nga e cila do të dërgojmë gramët në kontratën smart.

Adresa e kontratës smart përbëhet nga dy numra, një numër të plotë 32-bit që përfaqëson workchain dhe një numër unik 256-bit të padiskutueshëm në këtë workchain. Për shembull, -1 dhe 12345, këtë adresë do ta ruajmë në skedarin.

Kam kopjuar funksionin për ruajtjen e adresës nga 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

Le të shohim se si funksionon funksioni, kjo do të ndihmojë në kuptimin e funksionit Fift. Nisëm Fift në mënyrë interaktive.

~/TON/build/crypto/fift -i 

Së pari, ne vendosim në stek -1, 12345 dhe emrin e skedarit të ardhshëm "sender.addr":

-1 12345 "sender.addr" 

Hapi tjetër është të ekzekutojmë funksionin -rot, i cili zhvendos stekun, në këtë mënyrë, numri unik i kontratës smart ndodhet në krye të stekut:

"sender.addr" -1 12345

256 u>B konverton numrin 256-bit të padiskutueshëm në bytes.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap ndërron vendet e dy elementeve të sipërm të stekut.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B konverton numrin 32-bit të plotë në bytes.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ bashkon dy sekuenca bytes.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Përsëri swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Dhe më në fund, ekzekutohet shkrimi i bytes në skedar B>file. Pas kësaj, steku ynë është bosh. E ndalojmë Fift. Një skedar i quajtur sender.addr. Do ta transferojmë skedarin në dosjen e krijuar test/addresses/.

Do të shkruajmë një test të thjeshtë që do të dërgojë gramë në kontratën smart. Ja komiti.

Tani do të merremi me logjikën e lotarisë.

E para që bëjmë është të kontrollojmë nëse mesazhi bounced apo jo, nëse bounced, atëherë e injorojmë. bounced domethënë, kontrata do të kthejë gramët nëse ndodh ndonjë gabim. Ne nuk do të kthejmë gramët në rast se ndonjë gabim ndodh.

Kontrollojmë bilancin, nëse është më pak se gjysmë gram, atëherë thjesht pranojmë mesazhin dhe e injorojmë.

Pastaj, analizojmë adresën e kontratës smart nga e cila ka ardhur mesazhi.

Lexojmë të dhënat nga depozita dhe pastaj fshijmë bastet e vjetra nga historia nëse ka më shumë se njëzet. Për lehtësi, kam shkruar tre funksione shtesë pack_order(), unpack_order(), remove_old_orders().

Pastaj, shohim nëse balanca nuk mjafton për të bërë pagesat, atëherë e konsiderojmë si një ri-plotësim dhe e ruajmë plotësimin në orders.

Më në fund, arrijmë në thelbin e kontratës smart.

Së pari, nëse lojtari ka humbur, ne e ruajmë në historinë e basteve dhe kur shuma është më e madhe se 3 gramë, i dërgojmë 1/3 pronarit të kontratës smart.

Nëse lojtari ka fituar, atëherë dërgojmë dyfishin e shumës në adresën e lojtarit dhe më pas ruajmë informacionin mbi bastin në histori.

() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impure {
    var cs = in_msg_cell.begin_parse();
    int flags = cs~load_uint(4);  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
    if (flags & 1) { ;; ignore bounced
        return ();
    }
    if (order_amount < 500000000) { ;; just receive grams without changing state 
        return ();
    }
    slice src_addr_slice = cs~load_msg_addr();
    (int src_wc, int src_addr) = parse_std_addr(src_addr_slice);
    (int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
    orders = remove_old_orders(orders, order_seqno);
    if (balance() = 4) {
        builder order = pack_order(order_seqno, 3, now(), order_amount, src_wc, src_addr);
        orders~udict_set_builder(32, order_seqno, order);
        set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
        if (order_amount > 3000000000) {
            send_grams(owner_wc, owner_account_id, order_amount / 3);
        }
        return ();
    }
    send_grams(src_wc, src_addr, 2 * order_amount);
    builder order = pack_order(order_seqno, 2, now(), order_amount, src_wc, src_addr);
    orders~udict_set_builder(32, order_seqno, order);
    set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins + 1, incoming_amount, outgoing_amount + 2 * order_amount, owner_wc, owner_account_id, orders));
}

Këtu është gjithçka. Komiti i përkatshëm.

Tani mbetet e thjeshtë, do të krijojmë metoda get, në mënyrë që nga bota e jashtme të mund të marrim informacionin rreth gjendjes së kontratës (në fakt të lexojmë të dhënat nga depozita e kontratës smart).

Do të shtojmë metodat get. Rreth mënyrës si të marrim informacionin rreth kontratës smart do të shkruajmë më poshtë.

Akoma e kam harruar të shtoj kodin, i cili do të trajtojë kërkesën e parë që ndodh gjatë publikimit të kontratës smart. Komiti i përkatshëm. Po ashtu kam rregulluar bug me dërgimin 1/3 të shumës në llogarinë e pronarit.

Më pas mbetet të publikojmë kontratën smart. Le të krijojmë një dosje requests.

Si bazë, kam marrë kodin e publikimit simple-wallet-code.fc i cili mund të gjeni ndodhet në depo zyrtare.

Nga ajo që ia vlen të theksohet. Ne formojmë depotron e kontratës smart dhe mesazhin në hyrje. Pas kësaj, generohet adresa e kontratës smart, pra adresa është e njohur edhe para publikimit në TON. Më pas, në këtë adresë duhet të dërgohen disa gramë dhe vetëm pas kësaj duhet të dërgohet skedari me vetë kontratën smart, pasi rrjeti merr një komision për ruajtjen e kontratës smart dhe operacionet në të (validuesit që ruajnë dhe ekzekutojnë kontratat smart). Kodet mund të shihen këtu.

Pastaj ekzekutojmë kodin e publikimit dhe marrim lottery-query.boc skedarin dhe adresën e kontratës smart.

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

Mos harro të ruash skedarët e gjeneruar: lottery-query.boc, lottery.addr, lottery.pk.

Përveç të tjerave, në logun e ekzekutimit do të shohim adresën e kontratës smart.

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

Për interes, bëjmë një kërkesë në TON

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

Dhe do të shohim se llogaria me këtë adresë është bosh.

account state is empty

Dërgojmë në adresën 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram dhe pas disa sekondash ekzekutojmë të njëjtën komandë. Për dërgimin e gramëve po përdor portofolin zyrtar, ndërsa gramët testues mund t'i kërkosh nga ndonjë person në bisedë, për të cilin do flas në fund të artikullit.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Shikohet se në rrjet është shfaqur një kontratë smart e pa inicializuar (state:account_uninit) me këtë adresë dhe bilanc 1 000 000 000 nanogram.

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

Tani do të publikojmë kontratën smart. Do të nis dim lite-client dhe do e ekzekutojmë.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] dërgimi i pyetjes nga skedari lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    statusi i mesazhit të jashtëm është 1 

Do të verifikojmë nëse kontrata është publikuar.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Përveç të tjerave do të marrim.

  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

Shohim se account_active.

Komiti përkatës me ndryshimet këtu.

Tani të krijojmë kërkesat për ndërveprimin me kontratën smart.

Në fakt, kërkesën e parë për ndryshimin e adresës do ta lëmë si punë të vetme, ndërsa të dytën për dërgimin e gramëve në adresën e pronarit do ta bëjmë. Në fakt, ne duhet të bëjmë të njëjtën gjë si në testin për dërgimin e gramëve.

Ky është mesazhi që do të dërgojmë në kontratën smart, ku msg_seqno 165, veprim 2 dhe 9.5 gram për dërgim.

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

Mos harro të nënshkruash mesazhin me çelësin privat lottery.pk, i cili u gjenerua më parë kur u krijua kontrata smart. Ja komiti përkatës.

Marrim informacion nga kontrata smart përmes metodave të marrjes

Tani të shohim si të ekzekutojmë metodat e marrjes të kontratës smart.

Nisni lite-client dhe ekzekutojmë metodat e marrjes që kemi shkruar.

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

Në result përmban vlerën që kthen funksioni balance() nga kontrata jonë smart.
Të njëjtën gjë do ta bëjmë edhe për disa metoda të tjera.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
argumente:  [ 77871 ] 
rezultati:  [ 1 ] 

Do të kërkojmë historinë e basteve.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
argumente:  [ 67442 ] 
rezultati:  [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] 

Do të përdorim lite-client dhe metodat e marrjes për të shpërndarë informacionin mbi kontratën smart në faqen e internetit.

Të dhënat e kontratës smart shfaqen në faqe

Kam shkruar një faqe të thjeshtë në Python për të shfaqur të dhënat nga kontrata smart në një formë të përshtatshme. Këtu nuk do të ndalem në detaje dhe do ta publikoj faqen me një komit.

Kërkesat për TON bëhen nga Python me anë të lite-client. Për lehtësi, faqja paketizohet në Docker dhe publikohet në Google Cloud. Linku për faqen.

Le ta provojmë

Tani le të përpiqemi të dërgojmë gramë për të mbushur nga portofoli. Do të dërgojmë 40 gram. Dhe do të bëjmë disa oferta për të ilustruar. Shohim se faqja tregon historinë e ofertave, përqindjen aktuale të fitimeve dhe informacion tjetër të dobishëm.

Shohim, që e para e fituam, të dytën e humbëm.

Pasthënie

Artikulli doli shumë më i gjatë se sa kisha parashikuar, ndoshta mund të ishte më i shkurtër, ose ndoshta pikërisht për një person që nuk di asgjë rreth TON dhe dëshiron të shkruajë dhe publikojë një smart-kontakt jo aq të thjeshtë me mundësinë për të ndërvepruar me të. Ndoshta disa gjëra mund të ishin shpjeguar thjesht.

Ndoshta disa momente në realizim mund të ishin bërë më efektive dhe elegante, por atëherë do të ishte shpenzuar më shumë kohë për përgatitjen e artikullit. Po ashtu, është e mundur që ndoshta kam gabuar diku ose nuk e kam kuptuar diçka, prandaj nëse po bëni diçka serioze duhet të mbështeteni në dokumentacionin zyrtar ose në depozitarin zyrtar me kodin TON.

Duhet të theksoj se duke qenë se vetë TON është ende në fazën aktive të zhvillimit, mund të ndodhin ndryshime që do të thyjnë ndonjë nga hapat në këtë artikull (çka ndodhi ndërsa shkruaja, tashmë e kam riparuar), por qasja e përgjithshme nuk do të ndryshojë lehtë.

PĂ«r tĂ« ardhmen e TON nuk do tĂ« diskutoj. Ndoshta platforma do tĂ« bĂ«het diçka mĂ« e madhe dhe duhet tĂ« investojmĂ« kohĂ« nĂ« studimin e saj dhe tĂ« zĂ«mĂ« njĂ« niĆŸĂ« me produktet tona tani.

Ka edhe Libra nga Facebook, e cila ka një audiencë potenciale përdoruesish më të madhe se TON. Për Librën nuk di shumë, por sipas forumit, aktiviteti atje është shumë më i madh se në komunitetin TON. Edhe pse zhvilluesit dhe komuniteti i TON duken më shumë si një underground, gjë që është gjithashtu cool.

Linke

  1. Dokumentacioni zyrtar për TON: https://test.ton.org
  2. Depozitari zyrtar i TON: https://github.com/ton-blockchain/ton
  3. Portofoli zyrtar për platforma të ndryshme: https://wallet.ton.org
  4. Depozitari i smart-kontaktit nga ky artikull: https://github.com/raiym/astonished
  5. Linku në faqen e smart-kontaktit: https://ton-lottery.appspot.com
  6. Depozitari për zgjerimin për Visual Studio Code për FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Grupi në Telegram për TON, i cili ndihmoi shumë për të kuptuar në fazën fillestare. Mendoj se nuk do të ishte gabim të them se atje janë të gjithë ata që kanë shkruar diçka për TON. Po ashtu, atje mund të kërkoni gramë testues. https://t.me/tondev_ru
  8. Një tjetër grup për TON ku kam gjetur informacion të dobishëm: https://t.me/TONgramDev
  9. Faza e parë e konkursit: https://contest.com/blockchain
  10. Faza e dytë e konkursit: https://contest.com/blockchain-2

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster