Si të shkruani dhe publikoni një smart contract në Telegram Open Network (TON)

Si të shkruani dhe publikoni një smart contract në TON

Për çfarë bëhet fjalë në këtë artikull?

NĂ« kĂ«tĂ« artikull do tĂ« tregoj se si mora pjesĂ« nĂ« konkursin e parĂ« (nga dy) tĂ« Telegram pĂ«r blockchain, nuk arrita tĂ« fitoj njĂ« vend me çmim dhe vendosa ta dokumentoj kĂ«tĂ« pĂ«rvojĂ« nĂ« njĂ« artikull, qĂ« tĂ« mos humbasĂ« dhe, ndoshta, t’i hyjĂ« nĂ« punĂ« dikujt.

Meqë nuk doja të shkruaja kod abstrakt, por të krijoja diçka funksionale, për këtë artikull shkrova një smart contract për një lotari të menjëhershme dhe një faqe që shfaq të dhënat e smart contract-it direkt nga TON, pa përdorur ruajtje të ndërmjetme.

Artikulli do t’u vlejĂ« atyre qĂ« duan tĂ« krijojnĂ« smart contract-in e tyre tĂ« parĂ« nĂ« TON, por nuk dinĂ« nga t’ia nisin.

Duke marrë si shembull lotarinë, do të kaloj nga instalimi i mjedisit deri te publikimi i smart contract-it, ndërveprimi me të dhe do të shkruaj një faqe për marrjen dhe publikimin e të dhënave.

Rreth pjesëmarrjes në konkurs

NĂ« tetor tĂ« vitit tĂ« kaluar, Telegram shpalli njĂ« konkurs pĂ«r blockchain me gjuhĂ« tĂ« reja Fift dhe FunC. Duhej tĂ« shkruhej, sipas zgjedhjes, cilido nga pesĂ« smart contract-et e propozuara. M’u duk njĂ« ide e mirĂ« tĂ« merresha me diçka tĂ« pazakontĂ«, tĂ« mĂ«soja gjuhĂ«n dhe tĂ« krijoja diçka, edhe nĂ«se nĂ« tĂ« ardhmen nuk do tĂ« mĂ« duhej tĂ« shkruaja mĂ« asgjĂ« tjetĂ«r. PĂ«r mĂ« tepĂ«r, tema Ă«shtĂ« vazhdimisht nĂ« qendĂ«r tĂ« vĂ«mendjes.

Duhet thënë se nuk kisha përvojë në zhvillimin e smart contract-eve.

Planifikoja të merrja pjesë deri në fund, për aq kohë sa do të ishte e mundur, dhe më pas të shkruaja një artikull përmbledhës, por dështova që në të parin. Unë shkrova një portofol me multi-signature në FunC dhe ai, në përgjithësi, funksiononte. Si bazë mora smart contract në Solidity.

Në atë moment mendova se kjo ishte më se e mjaftueshme për të zënë të paktën një vend me çmim. Në fund, rreth 40 nga 60 pjesëmarrës u shpallën fitues dhe unë nuk isha mes tyre. Në përgjithësi, nuk ka asgjë të tmerrshme në këtë, por një gjë më shqetësoi. Në momentin kur u shpallën rezultatet, rishikimi me test për kontratën time nuk ishte bërë; pyeta pjesëmarrësit në chat nëse kishte edhe të tjerë që nuk e kishin, por të tillë nuk pati.

Me sa duket, dy ditĂ« pasi i kushtuan vĂ«mendje mesazheve tĂ« mia, gjyqtarĂ«t publikuan njĂ« koment, por ende nuk e kuptova nĂ«se gjatĂ« vlerĂ«simit e kishin anashkaluar pa dashje smart contract-in tim, apo thjesht menduan se ishte aq i dobĂ«t sa nuk meritonte koment. E bĂ«ra pyetjen nĂ« faqe, por nuk mora pĂ«rgjigje. Edhe pse nuk ishte sekret kush e kishte vlerĂ«suar, e konsiderova tĂ« panevojshme t’u shkruaja mesazhe private.

U shpenzua jo pak kohĂ« pĂ«r ta kuptuar, prandaj u vendos tĂ« shkruhej ky artikull. MeqĂ« pĂ«r momentin nuk ka shumĂ« informacion, ky material do t’u kursejĂ« kohĂ« tĂ« gjithĂ« tĂ« interesuarve.

Koncepti i funksionimit të smart contract-eve në TON

Para se tĂ« shkruajmĂ« diçka, duhet tĂ« kuptojmĂ« nga cila anĂ« t’i qasemi fare kĂ«saj gjĂ«je. Prandaj tani do tĂ« shpjegoj se nga cilat pjesĂ« pĂ«rbĂ«het sistemi. MĂ« saktĂ«, cilat pjesĂ« duhet tĂ« njihni qĂ« tĂ« shkruani tĂ« paktĂ«n njĂ« kontratĂ« funksionale.

Ne do të përqendrohemi te shkrimi i smart contract-it dhe puna me TON Virtual Machine (TVM), Fift dhe FunC, prandaj artikulli i ngjan më shumë një përshkrimi të zhvillimit të një programi të zakonshëm. Te mënyra se si funksionon vetë platforma nuk do të ndalemi këtu.

Në përgjithësi, për mënyrën se si funksionojnë TVM dhe gjuha Fift ka dokumentacion zyrtar të mirë. Gjatë pjesëmarrjes në konkurs dhe tani, gjatë shkrimit të kontratës aktuale, i jam referuar shpesh.

Gjuha kryesore në të cilën shkruhen smart contract-et është FunC. Aktualisht nuk ka dokumentacion për të, ndaj që të shkruani diçka duhet të studioni shembujt e smart contract-eve nga depoja zyrtare dhe vetë implementimin e gjuhës po aty; gjithashtu mund të shihni shembuj smart contract-esh nga dy konkurset e kaluara. Lidhjet janë në fund të artikullit.

Supozojmë se e kemi shkruar tashmë smart contract-in në FunC, pas kësaj e kompilohet kodi në assembler Fift.

Smart contract-i i kompiluar mbetet vetëm të publikohet. Për këtë duhet të shkruani një funksion në Fift, i cili do të marrë si hyrje kodin e smart contract-it dhe disa parametra të tjerë, ndërsa si dalje do të prodhojë një skedar me prapashtesën .boc (që do të thotë "bag of cells"), dhe, në varësi të mënyrës si e shkruajmë, çelësin privat dhe adresën që gjenerohet mbi bazën e kodit të smart contract-it. Në adresën e smart contract-it, edhe pse ende nuk është publikuar, tashmë mund të dërgohen grame.

Për të publikuar smart contract-in në TON, skedari i marrë .boc Skedari do të duhet të dërgohet në blockchain me ndihmën e lite-client (për këtë më poshtë). Por para publikimit duhet të transferoni grame në adresën e gjeneruar, përndryshe smart contract nuk do të publikohet. Pas publikimit, me smart contract do të mund të ndërveprohet duke i dërguar mesazhe nga jashtë (për shembull, me ndihmën e lite-client) ose nga brenda (për shembull, një smart contract i dërgon tjetrit një mesazh brenda TON).

Pasi kuptuam si publikohet kodi, më tej gjithçka bëhet më e thjeshtë. E dimë afërsisht çfarë duam të shkruajmë dhe si do të funksionojë programi ynë. Gjatë zhvillimit kërkojmë se si është realizuar kjo në smart contract-et ekzistuese, ose shohim drejtpërdrejt kodin e implementimit Fift dhe FunC në repository zyrtar, ose kontrollojmë dokumentacionin zyrtar.

Shumë shpesh kërkoja me fjalë kyçe në chat-in e Telegram ku ishin mbledhur të gjithë pjesëmarrësit e konkursit, përfshirë edhe punonjësit e Telegram. Kështu ndodhi që gjatë konkursit të gjithë u mblodhën pikërisht aty dhe filluan të diskutojnë Fift dhe FunC. Lidhja është në fund të artikullit.

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

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

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

Gjëja e parë që duhet të bëni është të shkarkoni dhe instaloni lite-client me të cilin mund të dërgoni kërkesa në TON.

Udhëzimi në faqen zyrtare e përshkruan procesin e instalimit mjaft qartë dhe në detaje, por lë jashtë disa hollësi. Këtu do ta ndjekim atë udhëzim, duke instaluar njëkohësisht varësitë që mungojnë. Nuk i kompilova vetë veçmas të gjitha projektet, por i instalova nga repository zyrtar i 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ë jenë instaluar të gjitha varësitë, mund të instaloni lite-client, Fift, FunC.

Së pari klonojmë repository e TON së bashku me varësitë. Për lehtësi, gjithçka do ta bëjmë në dosjen ~/TON.

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

NĂ« repository ruhen gjithashtu edhe implementimet e Fift dhe FunC.

Tani jemi gati të ndërtojmë projektin. Kodi i repository është klonuar në dosjen ~/TON/ton. Në ~/TON krijojmë dosjen build dhe ndërtojmë projektin në të.

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

Meqë do të shkruajmë një smart contract, na duhet jo vetëm lite-client, por edhe Fift me FunC, prandaj e kompilojmë gjithçka. Procesi nuk është i shpejtë, ndaj duhet të presim.

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

Më pas shkarkojmë skedarin e konfigurimit që përmban të dhënat për nodën me të cilën lite-client do të lidhet.

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

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

Tani le të nisim lite-client.

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

Nëse build-i përfundoi me sukses, atëherë pas nisjes do të shihni log-un e lidhjes së light client me nodën.

[ 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 se 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 e fundit të krijuar nga serveri. 

sendfile  dërgon në TON një skedar me mesazh; pikërisht me këtë komandë publikohet smart contract-i dhe dërgohen kërkesat drejt tij. 

getaccount  ngarkon gjendjen aktuale të smart contract-it me adresën e specifikuar. 

runmethod  []    ekzekuton GET-metodat e smart contract-it. 

Tani jemi gati të shkruajmë vetë kontratën.

Implementimi

Ideja

Siç e përmenda edhe më sipër, smart contract-i që po shkruajmë është një lotari.

Dhe kjo nuk është një lotari ku duhet të blesh një biletë dhe të presësh një orë, një ditë apo një muaj, por një lotari e menjëhershme ku përdoruesi transferon në adresën e kontratës N gram, dhe menjëherë merr mbrapsht 2 * N gram ose humbet. Probabilitetin e fitimit do ta bëjmë rreth 40%. Nëse nuk ka mjaftueshëm gram për pagesën, atëherë transaksionin do ta konsiderojmë si rimbushje balance.

Gjithashtu është e rëndësishme që bastet të mund të shihen në kohë reale dhe në një formë të përshtatshme, që përdoruesi ta kuptojë menjëherë nëse ka fituar apo ka humbur. Prandaj duhet të krijohet një faqe web që do të shfaqë bastet dhe rezultatin drejtpërdrejt nga TON.

Shkrimi i smart contract-it

Për lehtësi, unë kam bërë ndriçim sintakse për FunC; plugin-i mund të gjendet dhe të instalohet përmes kërkimit në Visual Studio Code. Nëse dëshironi të shtoni diçka, plugin-in e kam publikuar me qasje të hapur. Gjithashtu, më herët dikush ka bërë edhe një plugin për punë me Fift, i cili po ashtu mund të gjendet dhe të instalohet në VSC.

Le të krijojmë menjëherë një repository ku do të bëjmë commit rezultatet e ndërmjetme.

Për ta thjeshtuar punën, do ta shkruajmë smart contract-in dhe do ta testojmë lokalisht derisa të jetë gati. Vetëm pas kësaj do ta publikojmë në TON.

Smart contract-i ka dy metoda të jashtme që mund të thirren. E para, recv_external() ky funksion ekzekutohet kur kërkesa ndaj kontratës vjen nga jashtë, pra jo nga TON, për shembull kur e formojmë vetë mesazhin dhe e dërgojmë përmes lite-client. E dyta, recv_internal() kjo ndodh kur brenda vetë TON një kontratë i drejtohet kontratës sonë. Në të dyja rastet, funksionit mund t'i kalohen parametra.

Le të fillojmë me një shembull të thjeshtë, i cili do të funksionojë nëse publikohet, por nuk ka asnjë funksionalitet praktik.

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

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

Këtu duhet të sqarojmë se çfarë është slice. Të gjitha të dhënat e ruajtura në TON Blockchain janë një koleksion TVM cell ose thjesht cell, në një qelizë të tillë mund të ruhen deri në 1023 bit të dhënash dhe deri në 4 referenca drejt qelizave të tjera.

TVM cell slice ose slice është një pjesë e një cell ekzistuese dhe përdoret për ta parsuar; më poshtë do të bëhet më e qartë. Gjëja kryesore për ne është se në smart contract mund të kalojmë slice dhe, në varësi të llojit të mesazhit, t'i përpunojmë të dhënat në recv_external() ose recv_internal().

impure — fjalĂ« kyçe qĂ« tregon se funksioni ndryshon tĂ« dhĂ«nat e smart contract-it.

Le ta ruajmë kodin e kontratës në lottery-code.fc dhe ta kompilojmë.

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

Vlerat e flamujve mund t'i shihni me komandën

~\/TON\/build\/crypto\/func -help

Tani kemi marrë kodin e kompiluar Fift assembler 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ë lokalisht; për këtë le të përgatisim mjedisin.

Vini re se në rreshtin e parë përfshihet Asm.fif, ky është kod i shkruar në Fift për Fift assembler.

Meqë duam ta ekzekutojmë dhe testojmë smart contract-in lokalisht, le të krijojmë skedarin lottery-test-suite.fif dhe të kopjojmë aty kodin e kompiluar, duke zëvendësuar në të rreshtin e fundit që e shkruan kodin e smart contract-it në konstantën code, që më pas ta kalojmë në makinën virtuale:

"TonUtil.fif" include
"Asm.fif" include

PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:
  recv_external PROC:
}END>s constant code

Deri këtu gjithçka duket e qartë; tani le të shtojmë në të njëjtin skedar kodin që do të përdorim për të nisur TVM.

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

Në c7 Ne po regjistrojmë kontekstin, pra të dhënat me të cilat do të niset TVM (ose gjendja e rrjetit). Që gjatë konkursit, një nga zhvilluesit tregoi si krijohet c7 dhe unë e kopjova. Në këtë artikull mund të na duhet të ndryshojmë rand_seed sepse prej tij varet gjenerimi i numrit të rastësishëm; nëse nuk e ndryshojmë, çdo herë do të kthehet i njëjti numër.

recv_internal dhe recv_external Konstantet me vlerat 0 dhe -1 do të përdoren për të thirrur funksionet përkatëse në smart contract.

Tani jemi gati të krijojmë testin e parë për smart contract-in tonë bosh. Për qartësi, tani për tani të gjitha testet do t'i shtojmë në të njëjtin skedar lottery-test-suite.fif.

Le të krijojmë variablën storage dhe të shkruajmë në të një cellbosh; ky do të jetë ruajtja e smart contract-it.

message ky është mesazhi që do t'ia kalojmë smart contract-it nga jashtë. Edhe atë, për momentin, do ta lëmë bosh.

ruajtje e ndryshueshme 
<b b> ruajtje!

mesazh i ndryshueshëm 
<b b> mesazh! 

Pasi të kemi përgatitur konstantet dhe variablat, e nisim TVM me komandën runvmctx dhe i kalojmë si hyrje parametrat e krijuar.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Si rezultat do të marrim një kod të ndërmjetëm të tillë në Fift.

Tani mund ta nisim kodin e përftuar.

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

Programi duhet të ekzekutohet pa gabime dhe në dalje do të shohim log-un 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]     steps: 5 gas: used=304, max=9223372036854775807, limit=9223372036854775807, credit=0

Shkëlqyeshëm, kemi shkruar versionin e parë funksional të smart contract-it.

Tani duhet të shtojmë funksionalitet. Fillimisht do të merremi me mesazhet që vijnë nga bota e jashtme në recv_external()

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

Por zakonisht,

  • sĂ« pari, duam ta mbrojmĂ« kontratĂ«n tonĂ« nga bota e jashtme dhe tĂ« bĂ«jmĂ« qĂ« vetĂ«m pronari i kontratĂ«s tĂ« mund t'i dĂ«rgojĂ« asaj mesazhe tĂ« jashtme.
  • sĂ« dyti, kur dĂ«rgojmĂ« njĂ« mesazh tĂ« vlefshĂ«m nĂ« TON, duam qĂ« kjo tĂ« ndodhĂ« saktĂ«sisht njĂ« herĂ« dhe qĂ«, nĂ«se i njĂ«jti mesazh ridĂ«rgohet, smart contract-i ta refuzojĂ«.

Prandaj, pothuajse në çdo kontratë zgjidhen këto dy probleme. Meqë kontrata jonë pranon mesazhe të jashtme, duhet të kujdesemi edhe ne për këtë.

Do ta bëjmë në rend të kundërt. Fillimisht do të zgjidhim problemin e përsëritjes: nëse kontrata e ka marrë tashmë një mesazh të tillë dhe e ka përpunuar, ajo nuk do ta ekzekutojë për herë të dytë. Më pas do të zgjidhim edhe problemin që vetëm një rreth i caktuar personash të mund të dërgojnë mesazhe te smart contract.

Ka mënyra të ndryshme për të zgjidhur problemin e mesazheve të përsëritura. Ne do ta bëjmë kështu. Në smart contract inicializojmë numëruesin e mesazheve të pranuara me vlerën fillestare 0. Në çdo mesazh drejtuar smart contract do të shtojmë vlerën aktuale të numëruesit. Nëse vlera e numëruesit në mesazh nuk përputhet me vlerën në smart contract, nuk e përpunojmë; nëse përputhet, e përpunojmë dhe e rrisim numëruesin në smart contract me 1.

Kthehemi te lottery-test-suite.fif dhe i shtojmë testin e dytë. Do të dërgojmë një numër të pasaktë; kodi duhet të hedhë një përjashtim. Për shembull, le të ruhet 166 në të dhënat e kontratës, ndërsa ne do të dërgojmë 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"

E nisim.

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

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

[ 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

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

Tani le të shtojmë logjikën e numëruesit në smart contract te 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);
}

Në slice in_msg ndodhet mesazhi që po dërgojmë.

Gjëja e parë që bëjmë është të kontrollojmë nëse mesazhi përmban të dhëna; nëse jo, thjesht dalim.

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

Më pas ngarkojmë 32 bit nga ruajtja e smart contract. Kontrollojmë që numri i ngarkuar përputhet me atë të dërguar; nëse jo, hedhim një përjashtim. Në rastin tonë, meqë po dërgojmë një vlerë që nuk përputhet, duhet të hidhet përjashtim.

Tani le ta kompilojmë.

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

Kodën e përftuar e kopjojmë te lottery-test-suite.fif, pa harruar të zëvendësojmë rreshtin e fundit.

Verifikojmë që testi kalon:

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

Pikërisht këtu mund të shihni commit-in përkatës me rezultatet aktuale.

Vëmë re se kopjimi i vazhdueshëm i kodit të kompiluar të smart contract-it në skedarin e testeve nuk është praktik, prandaj do të shkruajmë një skript që do ta ruajë kodin në një konstante për ne, ndërsa ne thjesht do ta përfshijmë kodin e kompiluar në testet tona me anë të "include".

Në dosjen e projektit do të krijojmë skedarin 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ë të ekzekutueshëm.

chmod +x ./build.sh

Tani mjafton të ekzekutojmë skriptin tonë për të kompiluar kontratën. Por, përveç kësaj, duhet ta shkruajmë atë në konstanten code. Prandaj do të krijojmë një skedar të ri lotter-compiled-for-test.fif, të cilin më pas do ta përfshijmë në skedarin lottery-test-suite.fif.

Do tĂ« shtojmĂ« nĂ« skriptin sh kodin qĂ« thjesht do ta kopjojĂ« skedarin e kompiluar nĂ« lotter-compiled-for-test.fif dhe do t’i ndryshojĂ« rreshtin e 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 ta verifikuar, le të ekzekutojmë skriptin e krijuar dhe do të gjenerohet skedari lottery-compiled-for-test.fif, të cilin do ta përfshijmë në lottery-test-suite.fif

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

Ekzekutojmë testet për të verifikuar që kalojnë me sukses.

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

Shkëlqyeshëm, tani, për të automatizuar ekzekutimin e testeve, do të krijojmë skedarin test.sh, i cili fillimisht do të ekzekutojë build.sh, dhe më pas do të nisë testet.

touch test.sh
chmod +x test.sh

Brenda shkruajmë

./build.sh 

echo "nCompilation completedn"

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

E bĂ«jmĂ« test.sh dhe e ekzekutojmĂ« pĂ«r t’u siguruar qĂ« testet funksionojnĂ« siç duhet.

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

Verifikojmë që kontrata kompilohet dhe testet ekzekutohen.

Shkëlqyeshëm, tani gjatë ekzekutimit të test.sh do të kryhet menjëherë kompilimi dhe nisja e testeve. Ja lidhja për te komit.

Në rregull, para se të vazhdojmë, le të bëjmë edhe një gjë tjetër për lehtësi.

Do të krijojmë dosjen build ku do të ruajmë kontratën e kompiluar dhe klonin e saj të shkruar në një konstante lottery-compiled.fif, lottery-compiled-for-test.fif. Gjithashtu do të krijojmë dosjen test ku do të ruhet skedari me testet lottery-test-suite.fif dhe potencialisht skedarë të tjerë ndihmës. Lidhja për ndryshimet përkatëse.

Le të vazhdojmë zhvillimin e smart contract-it.

Më tej duhet të ketë një test që verifikon se mesazhi pranohet dhe numëruesi përditësohet në storage kur dërgojmë numrin e saktë. Por këtë do ta bëjmë më vonë.

Tani të mendojmë se çfarë strukture të dhënash dhe cilat të dhëna duhen ruajtur në smart contract.

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

`seqno` numër i plotë pozitiv 32-bitësh, numërues. 

`pubkey` numër i plotë pozitiv 256-bitësh, çelësi publik me të cilin do të verifikojmë nënshkrimin e mesazhit të dërguar nga jashtë, siç do ta shohim më poshtë. 

`order_seqno` numër i plotë pozitiv 32-bitësh që ruan numëruesin e sasisë së basteve. 

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

`incoming_amount` tipi i të dhënave Gram (4 bitët e parë përcaktojnë gjatësinë), ruan sasinë totale të grameve që janë dërguar në kontratë. 

`outgoing_amount` sasia totale e grameve që u është dërguar fituesve. 

`owner_wc` numri i workchain, numër i plotë 32-bitësh (në disa vende thuhet se është 8-bitësh). Aktualisht janë vetëm dy: -1 dhe 0. 

`owner_account_id` numër i plotë pozitiv 256-bitësh, adresa e kontratës në workchain-in aktual. 

`orders` variabël e tipit dictionary, ruan njëzet bastet e fundit. 

Më pas duhet të shkruajmë dy funksione. Të parin do ta quajmë pack_state(), i cili do të paketojë të dhënat për ruajtjen e mëvonshme në storage të smart contract. Të dytin do ta quajmë unpack_state() dhe ai do të lexojë dhe kthejë të dhënat nga storage.

_ 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ë smart contract. Do të marrim një një rezultat të ndërmjetëm.

PĂ«r tĂ« ruajtur tĂ« dhĂ«nat, duhet tĂ« thĂ«rrasim funksionin e integruar set_data() dhe ai do t’i shkruajĂ« tĂ« dhĂ«nat nga pack_state() nĂ« storage tĂ« smart contract.

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

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

Duhet të verifikojmë që mesazhi hyrës nga jashtë është nënshkruar nga pronari i kontratës (ose nga një përdorues tjetër që ka qasje në çelësin privat).

Kur publikojmë një smart contract, mund ta inicializojmë me të dhënat që na duhen në storage, të cilat do të ruhen për përdorim të mëvonshëm. Atje do të shkruajmë çelësin publik, që të mund të verifikohet se nënshkrimi i mesazhit hyrës është bërë me çelësin privat përkatës.

Para se të vazhdojmë, le të krijojmë një çelës privat dhe ta ruajmë në test/keys/owner.pk. Për këtë, nisni Fift në modalitet interaktiv dhe ekzekutoni katër komanda.

`newkeypair` gjeneron çelësin publik dhe privat dhe i vendos në stack. 

`drop` heq elementin e sipërm nga stack-u (në këtë rast çelësin publik)  

`.s` thjesht tregon çfarë ndodhet aktualisht në stack 

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

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

Do të krijojmë dosjen keys brenda dosjes test dhe aty do të ruajmë çelësin privat.

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

NĂ« dosjen aktuale shohim skedarin owner.pk.

Ne e heqim çelësin publik nga stack-u, sepse kur të jetë e nevojshme mund ta marrim nga çelësi privat.

Tani duhet të shkruajmë verifikimin e nënshkrimit. Le të fillojmë me testin. Së pari lexojmë çelësin privat nga skedari me funksionin file>B dhe e ruajmë në variablën owner_private_key, më pas me funksionin priv>pub e konvertojmë çelësin privat në publik dhe rezultatin e ruajmë në 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 !

Do të na duhen të dy çelësat.

Inicializojmë storage-in e smart contract me të dhëna arbitrare në të njëjtën renditje si në funksionin pack_state()dhe e ruajmë në variablën storage.

ndryshore owner_private_key
ndryshore owner_public_key 
ndryshore orders
ndryshore owner_wc
ndryshore owner_account_id

skedari "./keys/owner.pk"&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> storage !

Më pas do të përpilojmë një mesazh të nënshkruar; ai do të përmbajë vetëm nënshkrimin dhe vlerën e numëruesit.

Fillimisht krijojmë të dhënat që duam të dërgojmë, më pas i nënshkruajmë me çelësin privat dhe në fund formojmë mesazhin e nënshkruar.

variabël message_to_sign
variabël message_to_send
variabël 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> &lt;s !  

Si përfundim, mesazhi që do të dërgojmë në smart contract ruhet në variablën message_to_send, për funksionet hashu, ed25519_sign_uint mund të lexoni shihni dokumentacionin e Fift.

Dhe për të nisur testin, e thërrasim sërish.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

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

Tani le ta ekzekutojmë testin: ai do të dështojë, prandaj do ta ndryshojmë smart contract-in që të mund të pranojë mesazhe të këtij formati dhe të verifikojë nënshkrimin.

Së pari, lexojmë 512 bitët e nënshkrimit nga mesazhi dhe i ruajmë në një variabël, më pas lexojmë 32 bitët e variablës së numëruesit.

Meqenëse kemi një funksion për leximin e të dhënave nga ruajtja e smart contract-it, do ta përdorim atë.

Më pas kontrollojmë numëruesin e dërguar me atë në ruajtje dhe verifikojmë nënshkrimin. Nëse diçka nuk përputhet, 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));

Commit-i përkatës këtu.

Le të ekzekutojmë testet dhe do të shohim që testi i dytë dështon. Ka dy arsye: mungesë bitësh në mesazh dhe mungesë bitësh në ruajtje, prandaj kodi dështon gjatë parsimit. Duhet të shtojmë nënshkrimin e mesazhit që po dërgojmë dhe të kopjojmë ruajtjen nga testi i fundit.

Në testin e dytë do të shtojmë nënshkrimin e mesazhit dhe do të ndryshojmë ruajtjen e smart contract-it. Ja kështu Ja si duket aktualisht skedari me testet.

Le të shkruajmë testin e katërt, në të cilin do të dërgojmë një mesazh të nënshkruar me një çelës privat të huaj. Do të krijojmë edhe një çelës privat dhe do ta ruajmë në skedar not-owner.pk. Me këtë çelës privat do të nënshkruajmë mesazhin. Le të ekzekutojmë testet dhe të sigurohemi që të gjitha testet kalojnë. Commit në këtë moment.

Tani, më në fund, mund të kalojmë te zbatimi i logjikës së smart contract-it.
Në recv_external() Do të pranojmë dy lloje mesazhesh.

MeqenĂ«se kontrata jonĂ« do tĂ« grumbullojĂ« humbjet e lojtarĂ«ve, kĂ«to para duhet t’i transferohen krijuesit tĂ« lotarisĂ«. Adresa e portofolit tĂ« krijuesit tĂ« lotarisĂ« ruhet nĂ« storage gjatĂ« krijimit tĂ« kontratĂ«s.

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

Le të fillojmë me të parën. Fillimisht do të shkruajmë një test që do të kontrollojë se pas dërgimit të mesazhit, smart contract-i ruajti adresën e re në storage. Vini re se në mesazh, përveç numëruesit dhe adresës së re, dërgojmë edhe action një numër të plotë jo negativ 7-bitësh; në varësi të tij, do të zgjedhim se si ta përpunojmë mesazhin në smart contract.

<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ë shihet se si ndodh deserializimi i storage të smart contract-it storage në Fift. Deserializimi i variablave përshkruhet në dokumentacionin e Fift.

Lidhja për commit-in me shtimin e testit.

Nisim testin dhe sigurohemi që ai dështon. Tani do të shtojmë logjikën për ndryshimin e adresës së pronarit të lotarisë.

Në smart-contract vazhdojmë të pars-ojmë message, e lexojmë në action. Ju kujtojmë se do të kemi dy action: ndryshimin e adresës dhe dërgimin e gramëve.

Më pas lexojmë adresën e re të pronarit të kontratës dhe e ruajmë në storage.
Nisim testet dhe shohim që testi i tretë dështon. Dështon sepse kontrata tani pars-on shtesë 7 bit nga mesazhi, të cilat mungojnë në test. Le të shtojmë në mesazh një actionqë nuk ekziston. Nisim testet dhe shohim që të gjitha kalojnë. Këtu commit-i me ndryshimet. Shkëlqyeshëm.

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

SĂ« pari do tĂ« shkruajmĂ« testin. Do tĂ« shkruajmĂ« dy teste: njĂ« kur balanca nuk mjafton dhe tjetrin kur gjithçka duhet tĂ« kalojĂ« me sukses. Testet mund t’i shihni nĂ« kĂ«tĂ« commit.

Tani le ta plotësojmë kodin. Së pari do të shkruajmë dy metoda ndihmëse. E para është një get-metodë për të mësuar balancën aktuale të smart-contract.

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

Dhe e dyta është për dërgimin e gramëve në një smart-contract tjetër. Këtë metodë e kam kopjuar plotësisht nga një smart-contract tjetër.

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

Le t’i shtojmĂ« kĂ«to dy metoda nĂ« smart-contract dhe tĂ« shkruajmĂ« logjikĂ«n. SĂ« pari pars-ojmĂ« sasinĂ« e gramĂ«ve nga mesazhi. MĂ« pas kontrollojmĂ« balancĂ«n; nĂ«se nuk mjafton, hedhim pĂ«rjashtim. NĂ«se gjithçka Ă«shtĂ« nĂ« rregull, 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));

Ja kështu ja si duket smart-contract në këtë moment. Le të nisim testet dhe të sigurohemi që ato kalojnë.

Meqë ra fjala, për çdo mesazh të përpunuar, smart contract-it i zbritet komision sa herë. Që smart contract-i ta ekzekutojë kërkesën e mesazhit, pas kontrolleve bazë duhet të thërritet accept_message().

Tani tĂ« kalojmĂ« te mesazhet e brendshme. NĂ« praktikĂ«, ne vetĂ«m do tĂ« pranojmĂ« grame dhe, nĂ« rast fitoreje, do t’i kthejmĂ« lojtarit shumĂ«n e dyfishuar, ndĂ«rsa nĂ« rast humbjeje njĂ« tĂ« tretĂ«n pronarit.

Së pari, le të shkruajmë një test të thjeshtë. Për këtë do të na duhet adresa testuese e smart contract-it, nga e cila do të simulojmë dërgimin e grameve te smart contract-i.

Adresa e smart contract-it përbëhet nga dy numra: një numër i plotë 32-bitësh që përcakton workchain-in dhe një numër i plotë jo negativ 256-bitësh që është identifikuesi unik i llogarisë në këtë workchain. Për shembull, -1 dhe 12345; këtë adresë do ta ruajmë në skedar.

Funksionin për ruajtjen e adresës e kopjova 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 si funksionon ky funksion; kjo do të na ndihmojë të kuptojmë si punon Fift. Nisim Fift në modalitet interaktiv.

~/TON/build/crypto/fift -i 

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

-1 12345 "sender.addr" 

Në hapin tjetër ekzekutohet funksioni -rot, i cili zhvendos stack-un në mënyrë që në krye të stack-ut të jetë identifikuesi unik i smart contract-it:

"sender.addr" -1 12345

256 u>B konverton një numër të plotë jo negativ 256-bitësh në byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap ndërron vendet e dy elementeve të sipërm të stack-ut.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B konverton një numër të plotë 32-bitësh në byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ bashkon dy sekuenca byte-sh.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Sërish swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Dhe në fund kryhet shkrimi i byte-ve në skedar B>file. Pas kësaj stack-u ynë është bosh. E ndalojmë Fift. Në dosjen aktuale u krijua skedari sender.addr. Le ta zhvendosim skedarin në dosjen e krijuar test/addresses/.

Le të shkruajmë një test të thjeshtë që do të dërgojë grame te smart contract-i. Ja ku është commit-i.

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

GjĂ«ja e parĂ« qĂ« bĂ«jmĂ« Ă«shtĂ« tĂ« kontrollojmĂ« mesazhin bounced ose jo; nĂ«se bounced, atĂ«herĂ« e injorojmĂ«. bounced do tĂ« thotĂ« qĂ« kontrata do t’i kthejĂ« gramet nĂ«se ndodh ndonjĂ« gabim. Ne nuk do t’i kthejmĂ« gramet nĂ«se papritur ndodh njĂ« gabim.

Kontrollojmë bilancin; nëse është më i vogël se gjysmë grami, thjesht e pranojmë mesazhin dhe e injorojmë.

Më pas analizojmë adresën e smart contract nga i cili erdhi mesazhi.

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

Më pas kontrollojmë: nëse balanca nuk mjafton për pagesën, e konsiderojmë se kjo nuk është një bast, por një rimbushje, dhe e ruajmë rimbushjen në orders.

Tani, më në fund, thelbi i smart contract.

Së pari, nëse lojtari humbi, e ruajmë bastin e tij në histori dhe, nëse shuma është më e madhe se 3 gram, dërgojmë 1/3 te pronari i smart contract.

Nëse lojtari fitoi, i dërgojmë shumën e dyfishuar në adresën e tij dhe më pas ruajmë informacionin për 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));
}

Kaq është. Commit-i përkatës.

Tani mbetet pjesa e thjeshtë: të krijojmë get-metodat, që nga jashtë të mund të merret informacion për gjendjen e kontratës (në fakt, të lexohen të dhënat nga ruajtja e smart contract).

Shtojmë get-metodat. Për mënyrën se si merret informacioni për smart contract do të shkruajmë më poshtë.

Gjithashtu harrova të shtoj kodin që do të përpunojë kërkesën e parë, e cila ndodh gjatë publikimit të smart contract-it. Commit-i përkatës. Dhe gjithashtu rregullova gabimin me dërgimin e 1/3 së shumës në llogarinë e pronarit.

Më pas mbetet vetëm të publikojmë smart contract-in. Le të krijojmë dosjen requests.

Si bazë mora kodin e publikimit simple-wallet-code.fc i cili mund të gjenden ndodhet në repository-n zyrtar.

Nga gjërat që ia vlen të vihen re: ne formojmë storage-in e smart contract-it dhe mesazhin hyrës. Pas kësaj gjenerohet adresa e smart contract-it, pra adresa dihet që 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ë smart contract-in, sepse për ruajtjen e smart contract-it dhe operacionet në të rrjeti merr komision (validatorët që ruajnë dhe ekzekutojnë smart contract-et). Kodin mund ta shihni këtu.

Më pas ekzekutojmë kodin e publikimit dhe marrim skedarin lottery-query.boc dhe adresën e smart contract-it.

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

Mos harroni të ruani skedarët e gjeneruar: lottery-query.boc, lottery.addr, lottery.pk.

Ndër të tjera, në log-et e ekzekutimit do të shohim adresën e smart contract-it.

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 kuriozitet, le të bëjmë një kërkesë në TON

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

Dhe do të shohim që 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 tĂ« dĂ«rguar gram pĂ«rdor portofolin zyrtar, ndĂ«rsa gramĂ«t testues mund t’ia kĂ«rkoni dikujt nga chati, pĂ«r tĂ« cilin do tĂ« flas nĂ« fund tĂ« artikullit.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Shohim se në rrjet është shfaqur një smart contract i painicializuar (state:account_uninit) me këtë adresë dhe balancë 1 000 000 000 nanogram.

gjendja e llogarisë është (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_}
transaksioni i fundit lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
gjendja e llogarisë është 2000000000ng

Tani do të publikojmë smart contract-in. Nisim lite-client dhe ekzekutojmë.

> 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 

Le të kontrollojmë që kontrata është publikuar.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Ndër të tjera 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 që account_active.

Commit-i përkatës me ndryshimet këtu.

Tani do të krijojmë kërkesa për të ndërvepruar me smart contract-in.

Më saktë, kërkesën e parë për ndryshimin e adresës do ta lëmë si detyrë të pavarur, ndërsa të dytën për dërgimin e gramëve në adresën e pronarit do ta bëjmë. Në praktikë, do të na duhet të bëjmë të njëjtën gjë si në testin për dërgimin e gramëve.

Këtë mesazh do ta dërgojmë te smart contract-i, ku msg_seqno 165, action 2 dhe 9.5 gram për dërgim.

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

Mos harroni të nënshkruani mesazhin me çelësin privat lottery.pk, i cili u gjenerua më herët gjatë krijimit të smart contract-it. Ja commit-i përkatës.

Marrim informacion nga smart contract-i me ndihmën e get-metodave

Tani le të shohim si të ekzekutojmë get-metodat e smart contract-it.

Po e nisim lite-client dhe ekzekutojmë get-metodat që kemi shkruar.

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

Në rezultati përmban vlerën që kthen funksioni balance() nga smart contract-i ynë.
Të njëjtën gjë do ta bëjmë edhe për disa metoda të tjera.

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

Le të kërkojmë historikun e basteve.

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

Do të përdorim lite-client dhe metodat get për të shfaqur informacionin e smart contract-it në faqe.

Shfaqim të dhënat e smart contract-it në faqe

Kam shkruar një faqe të thjeshtë në Python për të shfaqur të dhënat nga smart contract-i në një formë të përshtatshme. Këtu nuk do të ndalem në detaje dhe do ta publikoj faqen me një commit të vetëm.

Kërkesat ndaj TON bëhen nga Python nëpërmjet lite-client. Për lehtësi, faqja paketohet në Docker dhe publikohet në Google Cloud. Lidhja e faqes.

Po provojmë

Tani le të provojmë të dërgojmë gramë atje për ta mbushur nga portofoli. Do të dërgojmë 40 gramë. Dhe do të bëjmë disa baste për qartësi. Shohim që faqja tregon historikun e basteve, përqindjen aktuale të fitimit dhe informacione të tjera të dobishme.

Shohim, që të parin e fituam, të dytin e humbëm.

Pasthënie

Artikulli doli shumë më i gjatë nga sa kisha menduar; ndoshta mund të ishte edhe më i shkurtër, ose ndoshta është pikërisht i përshtatshëm për dikë që nuk di asgjë për TON dhe dëshiron të shkruajë e të publikojë një smart contract jo edhe aq të thjeshtë, me mundësi ndërveprimi me të. Ndoshta disa gjëra mund të ishin shpjeguar më thjesht.

Ndoshta disa momente në implementim mund të ishin bërë më efikase dhe më elegante, por atëherë përgatitja e artikullit do të kishte marrë edhe më shumë kohë. Gjithashtu, është e mundur që diku të kem gabuar ose të mos kem kuptuar diçka, prandaj nëse po bëni diçka serioze, duhet të mbështeteni te dokumentacioni zyrtar ose te repozitori zyrtar i kodit të TON.

Duhet theksuar se, meqë vetë TON është ende në një fazë aktive zhvillimi, mund të ndodhin ndryshime që prishin ndonjë nga hapat e këtij artikulli (gjë që edhe ndodhi ndërsa po e shkruaja, por tashmë e kam rregulluar), megjithatë qasja e përgjithshme vështirë se do të ndryshojë.

Nuk do të spekuloj për të ardhmen e TON. Ndoshta platforma do të bëhet diçka më e madhe dhe ia vlen të investojmë kohë për ta studiuar dhe të zëmë vendin tonë me produktet tona që tani.

Ekziston edhe Libra nga Facebook, e cila ka një audiencë të mundshme përdoruesish më të madhe se TON. Për Libra di shumë pak; nga sa kuptohet nga forumi, aktiviteti atje është dukshëm më i lartë se në komunitetin TON. Megjithatë, zhvilluesit dhe komuniteti i TON ngjajnë më shumë me një skenë underground, gjë që është po ashtu mjaft interesante.

Linket

  1. Dokumentacioni zyrtar për TON: https://test.ton.org
  2. Depoja zyrtare e TON: https://github.com/ton-blockchain/ton
  3. Portofoli zyrtar për platforma të ndryshme: https://wallet.ton.org
  4. Depoja e smart contract-it nga ky artikull: https://github.com/raiym/astonished
  5. Lidhja për faqen e smart contract-it: https://ton-lottery.appspot.com
  6. Depoja e shtesës për Visual Studio Code për FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Chat për TON në Telegram, i cili më ndihmoi shumë për ta kuptuar në fazën fillestare. Mendoj se nuk do të jetë gabim nëse them se aty janë të gjithë ata që kanë shkruar diçka për TON. Po aty mund të kërkoni edhe grame testuese. https://t.me/tondev_ru
  8. Edhe një chat tjetër 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

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