Tungkol sa kung paano magsulat at mag-publish ng isang matalinong kontrata sa Telegram Open Network (TON)

Tungkol sa kung paano magsulat at mag-publish ng isang matalinong kontrata sa TON

Tungkol saan ang artikulong ito?

Sa artikulong pag-uusapan ko kung paano ako nakibahagi sa una (sa dalawa) kumpetisyon sa blockchain ng Telegram, hindi kumuha ng premyo, at nagpasyang itala ang aking karanasan sa isang artikulo upang hindi ito malunod sa limot at, marahil, tumulong. isang tao.

Dahil hindi ko nais na magsulat ng abstract code, ngunit upang gumawa ng isang bagay na gumagana, para sa artikulo ay sumulat ako ng isang matalinong kontrata para sa isang instant lottery at isang website na nagpapakita ng data ng matalinong kontrata nang direkta mula sa TON nang hindi gumagamit ng intermediate storage.

Ang artikulo ay magiging kapaki-pakinabang sa mga gustong gumawa ng kanilang unang matalinong kontrata sa TON, ngunit hindi alam kung saan magsisimula.

Gamit ang lottery bilang isang halimbawa, pupunta ako mula sa pag-install ng kapaligiran patungo sa pag-publish ng isang matalinong kontrata, pakikipag-ugnayan dito, at pagsusulat ng isang website para sa pagtanggap at pag-publish ng data.

Tungkol sa pakikilahok sa kompetisyon

Noong nakaraang Oktubre, inihayag ng Telegram ang isang kumpetisyon sa blockchain na may mga bagong wika Fift ΠΈ FunC. Kinailangang pumili mula sa pagsulat ng alinman sa limang iminungkahing smart contract. Naisip ko na masarap gumawa ng ibang bagay, matuto ng wika at gumawa ng isang bagay, kahit na hindi ko na kailangang magsulat ng kahit ano sa hinaharap. Dagdag pa, ang paksa ay patuloy na nasa labi.

Ito ay nagkakahalaga na sabihin na wala akong karanasan sa pagbuo ng mga matalinong kontrata.

Pinlano kong lumahok hanggang sa pinakadulo hanggang sa makakaya ko at pagkatapos ay magsulat ng isang artikulo sa pagsusuri, ngunit nabigo ako kaagad sa una. ako nagsulat ng wallet na may naka-on na multi-signature FunC at ito sa pangkalahatan ay nagtrabaho. Kinuha ko ito bilang batayan matalinong kontrata sa Solidity.

Sa oras na iyon, naisip ko na ito ay tiyak na sapat upang kumuha ng kahit ilang lugar ng premyo. Dahil dito, humigit-kumulang 40 sa 60 kalahok ang naging premyo at wala ako sa kanila. Sa pangkalahatan, walang mali dito, ngunit isang bagay ang bumabagabag sa akin. Sa oras ng pag-anunsyo ng mga resulta, ang pagsusuri ng pagsusulit para sa aking kontrata ay hindi pa tapos, tinanong ko ang mga kalahok sa chat kung may iba pa bang wala nito, wala.

Tila binibigyang pansin ang aking mga mensahe, makalipas ang dalawang araw ay naglathala ng komento ang mga hukom at hindi ko pa rin maintindihan kung hindi nila sinasadyang napalampas ang aking matalinong kontrata sa panahon ng paghusga o naisip lamang na ito ay napakasama na hindi na kailangan ng komento. Nagtanong ako ng isang katanungan sa pahina, ngunit hindi nakatanggap ng sagot. Bagama't hindi lihim kung sino ang nanghusga, itinuring kong hindi kailangan na magsulat ng mga personal na mensahe.

Maraming oras ang ginugol sa pag-unawa, kaya napagpasyahan na magsulat ng isang artikulo. Dahil wala pang maraming impormasyon, makakatulong ang artikulong ito na makatipid ng oras para sa lahat ng interesado.

Ang konsepto ng mga matalinong kontrata sa TON

Bago ka magsulat ng anuman, kailangan mong malaman kung saang panig lapitan ang bagay na ito. Samakatuwid, ngayon sasabihin ko sa iyo kung anong mga bahagi ang binubuo ng system. Mas tiyak, kung anong mga bahagi ang kailangan mong malaman upang magsulat ng hindi bababa sa ilang uri ng kontrata sa pagtatrabaho.

Magtutuon kami sa pagsulat ng isang matalinong kontrata at pakikipagtulungan TON Virtual Machine (TVM), Fift ΠΈ FunC, kaya ang artikulo ay mas katulad ng isang paglalarawan ng pagbuo ng isang regular na programa. Hindi namin tatalakayin kung paano gumagana ang platform mismo dito.

Sa pangkalahatan tungkol sa kung paano ito gumagana TVM at wika Fift may magandang opisyal na dokumentasyon. Habang sumasali sa kompetisyon at ngayon habang sinusulat ang kasalukuyang kontrata, madalas akong lumingon sa kanya.

Ang pangunahing wika kung saan nakasulat ang mga matalinong kontrata ay FunC. Walang dokumentasyon tungkol dito sa ngayon, kaya upang magsulat ng isang bagay kailangan mong pag-aralan ang mga halimbawa ng mga matalinong kontrata mula sa opisyal na imbakan at ang pagpapatupad ng mismong wika doon, at maaari kang tumingin sa mga halimbawa ng mga matalinong kontrata mula sa nakaraang dalawang mga kumpetisyon. Mga link sa dulo ng artikulo.

Sabihin nating nakapagsulat na kami ng matalinong kontrata para sa FunC, pagkatapos nito ay pinagsama-sama namin ang code sa Fift assembler.

Ang pinagsama-samang matalinong kontrata ay nananatiling mai-publish. Upang gawin ito kailangan mong magsulat ng isang function sa Fift, na kukuha ng smart contract code at ilang iba pang parameter bilang input, at ang output ay isang file na may extension .boc (na nangangahulugang "bag ng mga cell"), at, depende sa kung paano namin ito isinusulat, isang pribadong key at address, na nabuo batay sa smart contract code. Maaari ka nang magpadala ng mga gramo sa address ng isang matalinong kontrata na hindi pa nai-publish.

Upang mag-publish ng isang matalinong kontrata sa TON natanggap .boc ang file ay kailangang ipadala sa blockchain gamit ang isang light client (higit pa sa ibaba). Ngunit bago mag-publish, kailangan mong ilipat ang mga gramo sa nabuong address, kung hindi, ang matalinong kontrata ay hindi mai-publish. Pagkatapos ng paglalathala, maaari kang makipag-ugnayan sa smart contract sa pamamagitan ng pagpapadala dito ng mga mensahe mula sa labas (halimbawa, gamit ang isang light client) o mula sa loob (halimbawa, ang isang smart contract ay nagpapadala ng isa pang mensahe sa loob ng TON).

Kapag naiintindihan namin kung paano nai-publish ang code, nagiging mas madali ito. Halos alam namin kung ano ang gusto naming isulat at kung paano gagana ang aming programa. At habang nagsusulat, hinahanap namin kung paano ito ipinapatupad sa mga kasalukuyang smart contract, o tinitingnan namin ang code ng pagpapatupad Fift ΠΈ FunC sa opisyal na imbakan, o tumingin sa opisyal na dokumentasyon.

Kadalasan ay naghanap ako ng mga keyword sa Telegram chat kung saan nagtipon ang lahat ng kalahok sa kumpetisyon at mga empleyado ng Telegram, at nagkataon na sa panahon ng kumpetisyon lahat ay nagtipon doon at nagsimulang talakayin ang Fift at FunC. Link sa dulo ng artikulo.

Panahon na upang lumipat mula sa teorya patungo sa pagsasanay.

Inihahanda ang kapaligiran para sa pakikipagtulungan sa TON

Ginawa ko ang lahat na ilalarawan sa artikulo sa MacOS at i-double-check ito sa malinis na Ubuntu 18.04 LTS sa Docker.

Ang unang bagay na kailangan mong gawin ay i-download at i-install lite-client kung saan maaari kang magpadala ng mga kahilingan sa TON.

Ang mga tagubilin sa opisyal na website ay naglalarawan sa proseso ng pag-install nang detalyado at malinaw at nag-aalis ng ilang mga detalye. Narito sinusunod namin ang mga tagubilin, pag-install ng mga nawawalang dependency sa daan. Hindi ko naipon ang bawat proyekto sa aking sarili at na-install mula sa opisyal na repositoryo ng Ubuntu (sa MacOS na ginamit ko 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 

Kapag na-install na ang lahat ng dependencies maaari kang mag-install lite-client, Fift, FunC.

Una, kino-clone namin ang repositoryo ng TON kasama ang mga dependency nito. Para sa kaginhawahan, gagawin namin ang lahat sa isang folder ~/TON.

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

Nag-iimbak din ang repositoryo ng mga pagpapatupad Fift ΠΈ FunC.

Ngayon ay handa na kaming tipunin ang proyekto. Ang repository code ay na-clone sa isang folder ~/TON/ton. Sa ~/TON lumikha ng isang folder build at kolektahin ang proyekto sa loob nito.

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

Dahil magsusulat tayo ng isang matalinong kontrata, kailangan natin hindi lamang lite-clientpero Fift с FunC, kaya i-compile natin ang lahat. Hindi ito mabilis na proseso, kaya naghihintay kami.

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

Susunod, i-download ang configuration file na naglalaman ng data tungkol sa node kung saan lite-client magkokonekta.

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

Paggawa ng mga unang kahilingan sa TON

Ngayon ilunsad natin lite-client.

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

Kung matagumpay ang build, pagkatapos pagkatapos ng paglunsad ay makikita mo ang isang log ng koneksyon ng light client sa node.

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

Maaari mong patakbuhin ang utos help at tingnan kung anong mga utos ang magagamit.

help

Ilista natin ang mga utos na gagamitin natin sa artikulong ito.

list of available commands:
last    Get last block and state info from server
sendfile <filename> Load a serialized message from <filename> and send it to server
getaccount <addr> [<block-id-ext>]  Loads the most recent state of specified account; <addr> is in [<workchain>:]<hex-or-base64-addr> format
runmethod <addr> [<block-id-ext>] <method-id> <params>...   Runs GET method <method-id> of account <addr> with specified parameters

last ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ послСдний созданный Π±Π»ΠΎΠΊ с сСрвСра. 

sendfile <filename> отправляСт Π² TON Ρ„Π°ΠΉΠ» с сообщСниСм, ΠΈΠΌΠ΅Π½Π½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ этой ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ публикуСтся смарт-ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ ΠΈ запрсосы ΠΊ Π½Π΅ΠΌΡƒ. 

getaccount <addr> Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π΅ состояниС смарт-ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚Π° с ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΌ адрСсом. 

runmethod <addr> [<block-id-ext>] <method-id> <params>  запускаСт get-ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ смартконтракта. 

Ngayon ay handa na kaming isulat ang kontrata mismo.

Pagpapatupad

Idea

Tulad ng isinulat ko sa itaas, ang matalinong kontrata na sinusulat namin ay isang lottery.

Bukod dito, hindi ito isang lottery kung saan kailangan mong bumili ng tiket at maghintay ng isang oras, araw o buwan, ngunit isang instant kung saan ang gumagamit ay lumipat sa address ng kontrata. N gramo, at agad itong ibinalik 2 * N gramo o nawawala. Gagawin namin ang posibilidad na manalo ng humigit-kumulang 40%. Kung walang sapat na gramo para sa pagbabayad, isasaalang-alang namin ang transaksyon bilang isang top-up.

Bukod dito, mahalaga na ang mga taya ay makikita sa totoong oras at sa isang maginhawang anyo, upang agad na maunawaan ng gumagamit kung nanalo siya o natalo. Samakatuwid, kailangan mong gumawa ng website na magpapakita ng mga taya at resulta nang direkta mula sa TON.

Pagsusulat ng isang matalinong kontrata

Para sa kaginhawahan, na-highlight ko ang code para sa FunC; ang plugin ay makikita at mai-install sa paghahanap ng Visual Studio Code; kung bigla kang may gustong idagdag, ginawa kong available sa publiko ang plugin. Gayundin, ang isang tao ay dati nang gumawa ng isang plugin para sa pagtatrabaho sa Fift, maaari mo ring i-install ito at hanapin ito sa VSC.

Gumawa agad tayo ng repository kung saan ibibigay natin ang mga intermediate na resulta.

Upang gawing mas madali ang aming buhay, susulat kami ng isang matalinong kontrata at subukan ito nang lokal hanggang sa ito ay handa na. Pagkatapos lang nito ay ilalathala namin ito sa TON.

Ang matalinong kontrata ay may dalawang panlabas na pamamaraan na maaaring ma-access. Una, recv_external() ang pagpapaandar na ito ay isinasagawa kapag ang isang kahilingan sa kontrata ay nagmula sa labas ng mundo, iyon ay, hindi mula sa TON, halimbawa, kapag tayo mismo ay bumubuo ng isang mensahe at ipinadala ito sa pamamagitan ng lite-client. Pangalawa, recv_internal() ito ay kapag, sa loob mismo ng TON, ang anumang kontrata ay tumutukoy sa atin. Sa parehong mga kaso, maaari mong ipasa ang mga parameter sa function.

Magsimula tayo sa isang simpleng halimbawa na gagana kung nai-publish, ngunit walang functional load dito.

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

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

Dito kailangan nating ipaliwanag kung ano ito slice. Ang lahat ng data na nakaimbak sa TON Blockchain ay isang koleksyon TVM cell o simpleng cell, sa naturang cell maaari kang mag-imbak ng hanggang 1023 bits ng data at hanggang 4 na link sa iba pang mga cell.

TVM cell slice o slice ito ay bahagi ng umiiral na isa cell ay ginagamit upang i-parse ito, ito ay magiging malinaw sa ibang pagkakataon. Ang pangunahing bagay para sa amin ay maaari kaming lumipat slice at depende sa uri ng mensahe, iproseso ang data sa recv_external() o recv_internal().

impure β€” isang keyword na nagsasaad na binabago ng function ang data ng smart contract.

I-save natin ang code ng kontrata lottery-code.fc at mag-compile.

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

Ang kahulugan ng mga flag ay maaaring matingnan gamit ang command

~/TON/build/crypto/func -help

Nag-compile kami ng Fift assembler code in 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

Maaari itong ilunsad sa lokal, para dito ihahanda natin ang kapaligiran.

Tandaan na ang unang linya ay nag-uugnay Asm.fif, ito ay code na nakasulat sa Fift para sa Fift assembler.

Dahil gusto naming patakbuhin at subukan ang smart contract nang lokal, gagawa kami ng file lottery-test-suite.fif at kopyahin ang pinagsama-samang code doon, pinapalitan ang huling linya sa loob nito, na nagsusulat ng smart contract code sa isang pare-pareho codepara ilipat ito sa virtual machine:

"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

Sa ngayon ay tila malinaw, ngayon idagdag natin sa parehong file ang code na gagamitin natin upang ilunsad ang 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()

Π’ c7 itinatala namin ang konteksto, iyon ay, ang data kung saan ilulunsad ang TVM (o network state). Kahit na sa panahon ng kumpetisyon, ipinakita ng isa sa mga developer kung paano lumikha c7 at kinopya ko. Sa artikulong ito maaaring kailanganin nating baguhin rand_seed dahil ang henerasyon ng isang random na numero ay nakasalalay dito at kung hindi binago, ang parehong numero ay ibabalik sa bawat oras.

recv_internal ΠΈ recv_external Ang mga constant na may mga halagang 0 at -1 ay magiging responsable para sa pagtawag sa mga kaukulang function sa smart contract.

Ngayon ay handa na kaming gumawa ng unang pagsubok para sa aming walang laman na smart contract. Para sa kalinawan, sa ngayon ay idaragdag namin ang lahat ng mga pagsubok sa parehong file lottery-test-suite.fif.

Gumawa tayo ng variable storage at sumulat ng isang walang laman dito cell, ito ang magiging smart contract storage.

message Ito ang mensahe na ipapadala namin sa matalinong contact mula sa labas. Gagawin din natin itong walang laman sa ngayon.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Pagkatapos naming maihanda ang mga constant at variable, inilunsad namin ang TVM gamit ang command runvmctx at ipasa ang nilikha na mga parameter sa input.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Sa huli ay magtatagumpay tayo ganito intermediate code para sa Fift.

Ngayon ay maaari na nating patakbuhin ang resultang code.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняСм ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π· для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Ang programa ay dapat tumakbo nang walang mga error at sa output makikita natin ang execution log:

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

Mahusay, naisulat namin ang unang gumaganang bersyon ng matalinong kontrata.

Ngayon kailangan nating magdagdag ng pag-andar. Una, harapin natin ang mga mensahe na nagmumula sa labas ng mundo recv_external()

Ang developer mismo ang pumili ng format ng mensahe na maaaring tanggapin ng kontrata.

Pero kadalasan

  • una, gusto naming protektahan ang aming kontrata mula sa labas ng mundo at gawin ito upang ang may-ari lamang ng kontrata ang makakapagpadala ng mga panlabas na mensahe dito.
  • pangalawa, kapag nagpadala kami ng wastong mensahe sa TON, gusto naming mangyari ito nang eksaktong isang beses at kapag nagpadala kami muli ng parehong mensahe, tinatanggihan ito ng matalinong kontrata.

Kaya halos lahat ng kontrata ay nalulutas ang dalawang problemang ito, dahil ang aming kontrata ay tumatanggap ng mga panlabas na mensahe, kailangan din naming alagaan iyon.

Gagawin namin ito sa reverse order. Una, lutasin natin ang problema sa pag-uulit; kung ang kontrata ay nakatanggap na ng ganoong mensahe at naproseso ito, hindi na ito isasagawa sa pangalawang pagkakataon. At pagkatapos ay malulutas namin ang problema upang ang isang tiyak na lupon ng mga tao lamang ang makakapagpadala ng mga mensahe sa matalinong kontrata.

Mayroong iba't ibang paraan upang malutas ang problema sa mga duplicate na mensahe. Narito kung paano natin ito gagawin. Sa smart contract, sinisimulan namin ang counter ng mga natanggap na mensahe na may paunang halaga na 0. Sa bawat mensahe sa smart contract, idaragdag namin ang kasalukuyang counter value. Kung hindi tumutugma ang counter value sa mensahe sa value sa smart contract, hindi namin ito pinoproseso; kung ganoon, pinoproseso namin ito at tinataasan ng 1 ang counter sa smart contract.

Balik tayo sa lottery-test-suite.fif at magdagdag ng pangalawang pagsubok dito. Kung nagpadala kami ng maling numero, ang code ay dapat magtapon ng exception. Halimbawa, hayaan ang data ng kontrata na mag-store ng 166, at magpapadala kami ng 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"

Ilunsad natin.

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

At makikita natin na ang pagsubok ay naisakatuparan nang may error.

[ 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

Sa yugtong ito lottery-test-suite.fif dapat magmukhang ΠΏΠΎ ссылкС.

Ngayon, idagdag natin ang counter logic sa smart contract in lottery-code.fc.

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

() recv_external(slice in_msg) impure {
    if (slice_empty?(in_msg)) {
        return (); 
    }
    int msg_seqno = in_msg~load_uint(32);
    var ds = begin_parse(get_data());
    int stored_seqno = ds~load_uint(32);
    throw_unless(33, msg_seqno == stored_seqno);
}

Π’ slice in_msg kasinungalingan ang mensaheng ipinadala namin.

Ang unang bagay na gagawin namin ay suriin kung ang mensahe ay naglalaman ng data, kung hindi, pagkatapos ay lalabas lang kami.

Susunod na i-parse namin ang mensahe. in_msg~load_uint(32) nilo-load ang numerong 165, 32-bit unsigned int mula sa ipinadalang mensahe.

Susunod, naglo-load kami ng 32 bits mula sa smart contract storage. Sinusuri namin na ang na-load na numero ay tumutugma sa naipasa; kung hindi, naglalagay kami ng isang pagbubukod. Sa aming kaso, dahil kami ay pumasa sa isang hindi tugma, isang pagbubukod ang dapat ihagis.

Ngayon i-compile natin.

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

Kopyahin ang resultang code sa lottery-test-suite.fif, hindi nakakalimutang palitan ang huling linya.

Sinusuri namin na pumasa ang pagsubok:

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

Dito rito Maaari mong makita ang kaukulang commit sa mga kasalukuyang resulta.

Tandaan na hindi maginhawa na patuloy na kopyahin ang pinagsama-samang code ng isang matalinong kontrata sa isang file na may mga pagsubok, kaya susulat kami ng isang script na magsusulat ng code sa isang pare-pareho para sa amin, at ikokonekta lang namin ang pinagsama-samang code sa aming mga pagsubok gamit ang "include".

Lumikha ng isang file sa folder ng proyekto build.sh kasama ang sumusunod na nilalaman.

#!/bin/bash

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

Gawin natin itong executable.

chmod +x ./build.sh

Ngayon, patakbuhin lang ang aming script para i-compile ang kontrata. Ngunit bukod dito, kailangan nating isulat ito sa isang pare-pareho code. Kaya gagawa kami ng bagong file lotter-compiled-for-test.fif, na isasama namin sa file lottery-test-suite.fif.

Idagdag natin ang skirpt code sa sh, na magdo-duplicate lang sa pinagsama-samang file lotter-compiled-for-test.fif at baguhin ang huling linya dito.

# 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

Ngayon, upang suriin, patakbuhin natin ang resultang script at bubuo ng file lottery-compiled-for-test.fif, na isasama natin sa ating lottery-test-suite.fif

Π’ lottery-test-suite.fif tanggalin ang code ng kontrata at idagdag ang linya "lottery-compiled-for-test.fif" include.

Nagpapatakbo kami ng mga pagsubok upang suriin kung pumasa sila.

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

Mahusay, ngayon upang i-automate ang paglulunsad ng mga pagsubok, gumawa tayo ng file test.sh, na unang isasagawa build.sh, at pagkatapos ay patakbuhin ang mga pagsubok.

touch test.sh
chmod +x test.sh

Nagsusulat kami sa loob

./build.sh 

echo "nCompilation completedn"

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

Gawin natin test.sh at patakbuhin ito upang matiyak na gumagana ang mga pagsubok.

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

Sinusuri namin na ang kontrata ay nag-compile at ang mga pagsubok ay naisakatuparan.

Mahusay, ngayon sa startup test.sh Ang mga pagsubok ay isasama at tatakbo kaagad. Narito ang link sa mangako.

Okay, bago tayo magpatuloy, gawin natin ang isa pang bagay para sa kaginhawahan.

Gumawa tayo ng folder build kung saan iimbak namin ang kinopyang kontrata at ang clone nito na nakasulat sa isang pare-pareho lottery-compiled.fif, lottery-compiled-for-test.fif. Gumawa din tayo ng folder test saan itatabi ang test file? lottery-test-suite.fif at posibleng iba pang mga sumusuportang file. Mag-link sa mga nauugnay na pagbabago.

Ipagpatuloy natin ang pagbuo ng matalinong kontrata.

Susunod na dapat ay isang pagsubok na nagsusuri kung ang mensahe ay natanggap at ang counter ay na-update sa tindahan kapag ipinadala namin ang tamang numero. Pero mamaya na natin gagawin yun.

Ngayon, isipin natin kung anong istraktura ng data at kung anong data ang kailangang i-store sa matalinong kontrata.

Ilalarawan ko ang lahat ng iniimbak namin.

`seqno` 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число счСтчик. 

`pubkey` 256-Ρ‚ΠΈ Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡, с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ, ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ подпись ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΈΠ·Π²Π½Π΅ сообщСния, ΠΎ Ρ‡Π΅ΠΌ Π½ΠΈΠΆΠ΅. 

`order_seqno` 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число Ρ…Ρ€Π°Π½ΠΈΡ‚ счСтчик количСства ставок. 

`number_of_wins` 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число Ρ…Ρ€Π°Π½ΠΈΡ‚  количСство ΠΏΠΎΠ±Π΅Π΄. 

`incoming_amount` Ρ‚ΠΈΠΏ Π΄Π°Π½Π½Ρ‹Ρ… Gram (ΠΏΠ΅Ρ€Π²Ρ‹Π΅ 4 Π±ΠΈΡ‚Π° ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ Π·Π° Π΄Π»ΠΈΠ½Ρƒ), Ρ…Ρ€Π°Π½ΠΈΡ‚ ΠΎΠ±Ρ‰Π΅Π΅ количСство Π³Ρ€Π°ΠΌΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±Ρ‹Π»ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹ Π½Π° ΠΊΠΎΠ½Ρ‚Ρ€Ρ‚Π°ΠΊΡ‚. 

`outgoing_amount` ΠΎΠ±Ρ‰Π΅Π΅ количСство Π³Ρ€Π°ΠΌΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±Ρ‹Π»ΠΎ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½ΠΎ побСдитСлям. 

`owner_wc` Π½ΠΎΠΌΠ΅Ρ€ Π²ΠΎΡ€ΠΊΡ‡Π΅ΠΉΠ½Π°, 32-Ρ… Π±ΠΈΡ‚Π½ΠΎΠ΅ (Π² Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… мСстах написано, Ρ‡Ρ‚ΠΎ 8-ΠΌΠΈ Π±ΠΈΡ‚Π½ΠΎΠ΅) Ρ†Π΅Π»ΠΎΠ΅ число. Π’ Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ всСго Π΄Π²Π° -1 ΠΈ 0. 

`owner_account_id` 256-Ρ‚ΠΈ Π±ΠΈΡ‚Π½ΠΎΠ΅ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число, адрСс ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚Π° Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΌ Π²ΠΎΡ€ΠΊΡ‡Π΅ΠΉΠ½Π΅. 

`orders` пСрСмСнная Ρ‚ΠΈΠΏΠ° ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ, Ρ…Ρ€Π°Π½ΠΈΡ‚ послСдниС Π΄Π²Π°Π΄Ρ†Π°Ρ‚ΡŒ ставок. 

Susunod na kailangan mong magsulat ng dalawang pag-andar. Tawagan natin ang una pack_state(), na mag-iimpake ng data para sa kasunod na pag-save sa imbakan ng smart contract. Tawagan natin ang pangalawa unpack_state() magbabasa at magbabalik ng data mula sa imbakan.

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

Idinaragdag namin ang dalawang function na ito sa simula ng smart contract. Ito ay gagana ganito intermediate na resulta.

Upang i-save ang data kakailanganin mong tawagan ang built-in na function set_data() at ito ay magsusulat ng data mula sa pack_state() sa imbakan ng matalinong kontrata.

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

Ngayon na mayroon na kaming mga maginhawang function para sa pagsusulat at pagbabasa ng data, maaari na tayong magpatuloy.

Kailangan nating suriin na ang papasok na mensahe mula sa labas ay nilagdaan ng may-ari ng kontrata (o ibang user na may access sa pribadong key).

Kapag nag-publish kami ng matalinong kontrata, maaari naming simulan ito gamit ang data na kailangan namin sa storage, na ise-save para magamit sa hinaharap. Ire-record namin ang public key doon para ma-verify namin na ang papasok na mensahe ay nilagdaan gamit ang kaukulang pribadong key.

Bago magpatuloy, gumawa tayo ng pribadong key at isulat ito sa test/keys/owner.pk. Upang gawin ito, ilunsad natin ang Fift sa interactive mode at isagawa ang apat na command.

`newkeypair` гСнСрация ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΈ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΈ запись ΠΈΡ… Π² стСк. 

`drop` удалСния ΠΈΠ· стСка Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ элСмСнта (Π² Π΄Π°Π½Π½ΠΎΠΌ случаС ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡)  

`.s` просто ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ Π»Π΅ΠΆΠΈΡ‚ Π² стСкС Π² Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ 

`"owner.pk" B>file` запись ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° Π² Ρ„Π°ΠΉΠ» с ΠΈΠΌΠ΅Π½Π΅ΠΌ `owner.pk`. 

`bye` Π·Π°Π²Π΅Ρ€ΡˆΠ°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ с Fift. 

Gumawa tayo ng folder keys sa loob ng folder test at isulat ang pribadong susi doon.

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

Nakikita namin ang isang file sa kasalukuyang folder owner.pk.

Inalis namin ang pampublikong susi sa stack at kapag kinakailangan ay makukuha namin ito mula sa pribado.

Ngayon ay kailangan nating magsulat ng signature verification. Magsimula tayo sa pagsubok. Una naming basahin ang pribadong key mula sa file gamit ang function file>B at isulat ito sa isang variable owner_private_key, pagkatapos ay ginagamit ang function priv>pub i-convert ang pribadong susi sa isang pampublikong susi at isulat ang resulta sa 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 !

Kakailanganin natin ang parehong mga susi.

Sinisimulan namin ang smart contract storage gamit ang arbitrary na data sa parehong pagkakasunud-sunod tulad ng sa function pack_state()at isulat ito sa isang variable storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

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

<b 0 32 u, owner_public_key @ B, 0 32 u, 0 32 u, 0 Gram, 0 Gram, owner_wc @ 32 i, owner_account_id @ 256 u,  orders @ dict, b> storage !

Susunod, bubuo kami ng isang nilagdaang mensahe, naglalaman lamang ito ng pirma at halaga ng counter.

Una, ginagawa namin ang data na gusto naming ipadala, pagkatapos ay nilagdaan namin ito gamit ang isang pribadong key at sa wakas ay bumubuo kami ng isang nilagdaang mensahe.

variable message_to_sign
variable message_to_send
variable signature
<b 0 32 u, b> message_to_sign !
message_to_sign @ hashu owner_private_key @ ed25519_sign_uint signature !
<b signature @ B, 0 32 u, b> <s  message_to_send !  

Bilang resulta, ang mensahe na ipapadala namin sa matalinong kontrata ay naitala sa isang variable message_to_send, tungkol sa mga function hashu, ed25519_sign_uint mababasa mo sa dokumentasyon ng Fift.

At para patakbuhin ang pagsubok, muli tayong tumawag.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Narito ito Ang file na may mga pagsubok ay dapat magmukhang ganito sa yugtong ito.

Patakbuhin natin ang pagsubok at ito ay mabibigo, kaya't babaguhin natin ang matalinong kontrata upang makatanggap ito ng mga mensahe ng ganitong format at ma-verify ang lagda.

Una, binibilang namin ang 512 bits ng lagda mula sa mensahe at isulat ito sa isang variable, pagkatapos ay binibilang namin ang 32 bits ng counter variable.

Dahil mayroon kaming function para sa pagbabasa ng data mula sa smart contract storage, gagamitin namin ito.

Susunod ay ang pagsuri sa counter na inilipat kasama ang imbakan at pagsuri sa pirma. Kung ang isang bagay ay hindi tumutugma, pagkatapos ay magtapon kami ng isang pagbubukod na may naaangkop na code.

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

Kaugnay na pangako dito.

Patakbuhin natin ang mga pagsubok at tingnan na nabigo ang pangalawang pagsubok. Para sa dalawang dahilan, walang sapat na bits sa mensahe at walang sapat na bits sa storage, kaya nag-crash ang code kapag nag-parse. Kailangan naming magdagdag ng lagda sa mensaheng ipinapadala namin at kopyahin ang storage mula sa huling pagsubok.

Sa pangalawang pagsubok, magdaragdag kami ng signature ng mensahe at babaguhin ang storage ng smart contract. Narito ito ang file na may mga pagsubok ay mukhang sa ngayon.

Sumulat tayo ng pang-apat na pagsubok, kung saan magpapadala tayo ng mensaheng nilagdaan gamit ang pribadong key ng ibang tao. Gumawa tayo ng isa pang pribadong key at i-save ito sa isang file not-owner.pk. Pipirmahan namin ang mensahe gamit ang pribadong key na ito. Patakbuhin natin ang mga pagsubok at siguraduhing makapasa ang lahat ng pagsubok. Mangako sa sandaling ito.

Ngayon ay maaari na tayong magpatuloy sa pagpapatupad ng matalinong lohika ng kontrata.
Π’ recv_external() tatanggap kami ng dalawang uri ng mensahe.

Dahil ang aming kontrata ay mag-iipon ng mga pagkalugi ng mga manlalaro, ang perang ito ay dapat ilipat sa lumikha ng lottery. Ang address ng pitaka ng tagalikha ng lottery ay naitala sa imbakan kapag ginawa ang kontrata.

Kung sakali, kailangan namin ng kakayahang baguhin ang address kung saan kami nagpadala ng mga gramo ng mga natalo. Dapat din tayong magpadala ng mga gramo mula sa lottery sa address ng may-ari.

Magsimula tayo sa una. Sumulat muna tayo ng pagsubok na susuriin na pagkatapos ipadala ang mensahe, nai-save ng smart contract ang bagong address sa storage. Pakitandaan na sa mensahe, bilang karagdagan sa counter at bagong address, ipinapadala din namin action Isang 7-bit na integer na hindi negatibong numero, depende dito, pipiliin namin kung paano iproseso ang mensahe sa smart contract.

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

Sa pagsubok, makikita mo kung paano na-deserialize ang smartcontract storage storage sa Fift. Ang deserialization ng mga variable ay inilarawan sa dokumentasyon ng Fift.

Mag-commit ng link na may idinagdag na kuwarta.

Patakbuhin natin ang pagsubok at siguraduhing mabibigo ito. Ngayon magdagdag tayo ng lohika upang baguhin ang address ng may-ari ng lottery.

Sa matalinong kontrata ay patuloy kaming nag-parse message, basahin sa action. Let us remind you na magkakaroon tayo ng dalawa action: baguhin ang address at magpadala ng mga gramo.

Pagkatapos ay binasa namin ang bagong address ng may-ari ng kontrata at i-save ito sa imbakan.
Pinapatakbo namin ang mga pagsubok at nakita namin na nabigo ang ikatlong pagsubok. Nag-crash ito dahil sa ang katunayan na ang kontrata ngayon ay nag-parse ng 7 bits mula sa mensahe, na nawawala sa pagsubok. Magdagdag ng hindi umiiral sa mensahe action. Patakbuhin natin ang mga pagsubok at tingnan na ang lahat ay pumasa. Dito mangako sa mga pagbabago. Malaki.

Ngayon isulat natin ang lohika para sa pagpapadala ng tinukoy na bilang ng mga gramo sa dating na-save na address.

Una, magsulat tayo ng isang pagsubok. Magsusulat kami ng dalawang pagsubok, isa kapag walang sapat na balanse, ang pangalawa kapag ang lahat ay dapat na matagumpay na pumasa. Maaaring matingnan ang mga pagsubok sa commit na ito.

Ngayon idagdag natin ang code. Una, sumulat tayo ng dalawang paraan ng katulong. Ang unang paraan ng pagkuha ay upang malaman ang kasalukuyang balanse ng isang matalinong kontrata.

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

At ang pangalawa ay para sa pagpapadala ng mga gramo sa isa pang matalinong kontrata. Ganap kong kinopya ang paraang ito mula sa isa pang matalinong kontrata.

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

Idagdag natin ang dalawang pamamaraang ito sa matalinong kontrata at isulat ang lohika. Una, i-parse namin ang bilang ng mga gramo mula sa mensahe. Susunod na suriin namin ang balanse, kung ito ay hindi sapat na magtapon kami ng isang pagbubukod. Kung maayos ang lahat, ipinapadala namin ang mga gramo sa naka-save na address at i-update ang counter.

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

Narito ito mukhang ang matalinong kontrata sa ngayon. Patakbuhin natin ang mga pagsubok at siguraduhing makapasa sila.

Sa pamamagitan ng paraan, ang isang komisyon ay ibabawas mula sa matalinong kontrata sa bawat oras para sa isang naprosesong mensahe. Upang maisakatuparan ng mga mensahe ng matalinong kontrata ang kahilingan, pagkatapos ng mga pangunahing pagsusuri kailangan mong tumawag accept_message().

Ngayon ay lumipat tayo sa mga panloob na mensahe. Sa katunayan, tatanggap lamang kami ng gramo at ibabalik ang dobleng halaga sa manlalaro kung siya ay nanalo at isang pangatlo sa may-ari kung siya ay matalo.

Una, magsulat tayo ng isang simpleng pagsubok. Para magawa ito, kailangan namin ng pansubok na address ng smart contract kung saan nagpapadala kami ng mga gramo sa smart contract.

Ang smart contract address ay binubuo ng dalawang numero, isang 32-bit integer na responsable para sa workchain at isang 256-bit non-negative na integer na natatanging account number sa workchain na ito. Halimbawa, -1 at 12345, ito ang address na ise-save namin sa isang file.

Kinopya ko ang function para sa pag-save ng address mula sa 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

Tingnan natin kung paano gumagana ang function, magbibigay ito ng pag-unawa sa kung paano gumagana ang Fift. Ilunsad ang Fift sa interactive na mode.

~/TON/build/crypto/fift -i 

Una naming itulak ang -1, 12345 at ang pangalan ng hinaharap na file na "sender.addr" sa stack:

-1 12345 "sender.addr" 

Ang susunod na hakbang ay upang isagawa ang function -rot, na naglilipat sa stack sa paraang sa tuktok ng stack ay mayroong natatanging numero ng matalinong kontrata:

"sender.addr" -1 12345

256 u>B nagko-convert ng 256-bit non-negative integer sa bytes.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap pinapalitan ang dalawang nangungunang elemento ng stack.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B nagko-convert ng 32-bit integer sa bytes.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ nag-uugnay ng dalawang sequence ng bytes.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Muli swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

At sa wakas ang mga byte ay nakasulat sa file B>file. Pagkatapos nito ay walang laman ang aming stack. Huminto kami Fift. Isang file ang nagawa sa kasalukuyang folder sender.addr. Ilipat natin ang file sa ginawang folder test/addresses/.

Sumulat tayo ng isang simpleng pagsubok na magpapadala ng mga gramo sa isang matalinong kontrata. Narito ang pangako.

Ngayon tingnan natin ang lohika ng lottery.

Ang unang bagay na ginagawa namin ay suriin ang mensahe bounced o hindi kung bounced, pagkatapos ay hindi namin ito pinansin. bounced nangangahulugan na ang kontrata ay magbabalik ng gramo kung may nangyaring error. Hindi kami magbabalik ng gramo kung biglang may error.

Sinusuri namin, kung ang balanse ay mas mababa sa kalahating gramo, pagkatapos ay tinatanggap lang namin ang mensahe at huwag pansinin ito.

Susunod, i-parse namin ang address ng smart contract kung saan nanggaling ang mensahe.

Binabasa namin ang data mula sa imbakan at pagkatapos ay tanggalin ang mga lumang taya mula sa kasaysayan kung mayroong higit sa dalawampu sa kanila. Para sa kaginhawahan, sumulat ako ng tatlong karagdagang pag-andar pack_order(), unpack_order(), remove_old_orders().

Susunod, tinitingnan namin kung ang balanse ay hindi sapat para sa pagbabayad, pagkatapos ay isinasaalang-alang namin na ito ay hindi isang taya, ngunit isang muling pagdadagdag at i-save ang muling pagdadagdag sa orders.

Pagkatapos ay sa wakas ang kakanyahan ng matalinong kontrata.

Una, kung matalo ang manlalaro, ise-save namin ito sa kasaysayan ng pagtaya at kung ang halaga ay higit sa 3 gramo, ipinapadala namin ang 1/3 sa may-ari ng matalinong kontrata.

Kung manalo ang manlalaro, ipapadala namin ang dobleng halaga sa address ng manlalaro at pagkatapos ay i-save ang impormasyon tungkol sa taya sa kasaysayan.

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

Iyan na ang lahat. Kaukulang pangako.

Ngayon, simple na lang ang natitira, gumawa tayo ng mga get-method para makakuha tayo ng impormasyon tungkol sa estado ng kontrata mula sa labas ng mundo (sa katunayan, basahin ang data mula sa kanilang smart contract storage).

Magdagdag tayo ng get method. Magsusulat kami sa ibaba tungkol sa kung paano makatanggap ng impormasyon tungkol sa isang matalinong kontrata.

Nakalimutan ko ring idagdag ang code na magpoproseso sa pinakaunang kahilingan na nangyayari kapag nag-publish ng isang matalinong kontrata. Kaukulang pangako. At higit pa naitama bug sa pagpapadala ng 1/3 ng halaga sa account ng may-ari.

Ang susunod na hakbang ay i-publish ang matalinong kontrata. Gumawa tayo ng folder requests.

Kinuha ko ang publication code bilang batayan simple-wallet-code.fc kung saan mahahanap sa opisyal na imbakan.

Isang bagay na dapat bigyang pansin. Bumubuo kami ng smart contract storage at isang input message. Pagkatapos nito, nabuo ang address ng smart contract, iyon ay, ang address ay kilala bago pa man mailathala sa TON. Pagkatapos ay kailangan mong magpadala ng ilang gramo sa address na ito, at pagkatapos lamang nito kailangan mong magpadala ng isang file na may mismong matalinong kontrata, dahil ang network ay kumukuha ng isang komisyon para sa pag-iimbak ng matalinong kontrata at mga operasyon dito (mga validator na nag-iimbak at nagsasagawa ng mga matalinong kontrata ). Maaaring matingnan ang code dito.

Susunod na isagawa namin ang publishing code at makuha lottery-query.boc matalinong file ng kontrata at address.

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

Huwag kalimutang i-save ang nabuong mga file: lottery-query.boc, lottery.addr, lottery.pk.

Sa iba pang mga bagay, makikita natin ang address ng smart contract sa mga execution log.

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

Katuwaan lang, mag request tayo kay TON

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

At makikita natin na walang laman ang account na may ganitong address.

account state is empty

Ipinapadala namin sa address 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram at pagkatapos ng ilang segundo ay ipapatupad namin ang parehong utos. Upang magpadala ng mga gramo na ginagamit ko opisyal na wallet, at maaari kang magtanong sa isang tao mula sa chat para sa mga gramo ng pagsubok, na tatalakayin ko sa dulo ng artikulo.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Mukhang isang hindi nasimulan (state:account_uninit) isang matalinong kontrata na may parehong address at balanseng 1 nanograms.

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

Ngayon, i-publish natin ang matalinong kontrata. Ilunsad natin ang lite-client at i-execute.

> 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 

Tingnan natin kung nai-publish na ang kontrata.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Sa iba pang mga bagay na nakukuha namin.

  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

Nakikita natin yan account_active.

Kaugnay na pangako na may mga pagbabago dito.

Ngayon, gumawa tayo ng mga kahilingan para makipag-ugnayan sa smart contract.

Mas tiyak, iiwan namin ang una para sa pagpapalit ng address bilang isang independiyenteng gawain, at gagawin namin ang pangalawa para sa pagpapadala ng mga gramo sa address ng may-ari. Sa katunayan, kakailanganin nating gawin ang parehong bagay tulad ng sa pagsubok para sa pagpapadala ng mga gramo.

Ito ang mensaheng ipapadala namin sa smart contract, kung saan msg_seqno 165, action 2 at 9.5 gramo para sa pagpapadala.

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

Huwag kalimutang lagdaan ang mensahe gamit ang iyong pribadong key lottery.pk, na nabuo nang mas maaga nang gumawa ng matalinong kontrata. Narito ang kaukulang commit.

Pagtanggap ng impormasyon mula sa isang matalinong kontrata gamit ang get method

Ngayon tingnan natin kung paano patakbuhin ang mga pamamaraan ng smart contract get.

Ilunsad lite-client at patakbuhin ang get method na isinulat namin.

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

Π’ result naglalaman ng halaga na ibinabalik ng function balance() mula sa aming matalinong kontrata.
Gagawin namin ang parehong para sa ilang higit pang mga pamamaraan.

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

Tanungin natin ang kasaysayan ng iyong taya.

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

Gagamit kami ng lite-client at kukuha kami ng mga paraan upang magpakita ng impormasyon tungkol sa matalinong kontrata sa site.

Pagpapakita ng data ng matalinong kontrata sa website

Sumulat ako ng isang simpleng website sa Python upang ipakita ang data mula sa matalinong kontrata sa isang maginhawang paraan. Dito ay hindi ko ito tatalakayin nang detalyado at ilalathala ang site sa isang commit.

Ang mga kahilingan sa TON ay ginawa mula sa Python sa tulong lite-client. Para sa kaginhawahan, ang site ay naka-package sa Docker at na-publish sa Google Cloud. Link.

Sinusubukan

Ngayon subukan nating magpadala ng mga gramo doon para sa muling pagdadagdag mula sa wallet. Magpapadala kami ng 40 gramo. At gumawa tayo ng ilang taya para sa kalinawan. Nakita namin na ang site ay nagpapakita ng kasaysayan ng mga taya, ang kasalukuyang porsyento ng panalong at iba pang kapaki-pakinabang na impormasyon.

Nakikita naminna nanalo kami sa una, natalo sa pangalawa.

afterword

Ang artikulo ay naging mas mahaba kaysa sa inaasahan ko, marahil ito ay maaaring mas maikli, o marahil para lamang sa isang taong walang alam tungkol sa TON at gustong magsulat at mag-publish ng hindi gaanong simpleng kontrata na may kakayahang makipag-ugnayan sa ito. Marahil ang ilang mga bagay ay maaaring naipaliwanag nang mas simple.

Marahil ang ilang mga aspeto ng pagpapatupad ay maaaring gawin nang mas mahusay at eleganteng, ngunit pagkatapos ay tumagal ng mas maraming oras upang ihanda ang artikulo. Posible rin na nagkamali ako sa isang lugar o hindi naiintindihan ang isang bagay, kaya kung gumagawa ka ng isang bagay na seryoso, kailangan mong umasa sa opisyal na dokumentasyon o sa opisyal na imbakan na may TON code.

Dapat pansinin na dahil ang TON mismo ay nasa aktibong yugto pa ng pag-unlad, maaaring mangyari ang mga pagbabago na makakasira sa alinman sa mga hakbang sa artikulong ito (na nangyari habang nagsusulat ako, naitama na ito), ngunit ang pangkalahatang diskarte ay malabong magbago.

Hindi ako magsasalita tungkol sa kinabukasan ng TON. Marahil ang platform ay magiging isang bagay na malaki at dapat tayong gumugol ng oras sa pag-aaral nito at punan ang isang angkop na lugar sa ating mga produkto ngayon.

Mayroon ding Libra mula sa Facebook, na may potensyal na madla ng mga user na mas malaki kaysa sa TON. Halos wala akong alam tungkol sa Libra, sa paghusga sa forum, mas maraming aktibidad doon kaysa sa komunidad ng TON. Kahit na ang mga developer at komunidad ng TON ay mas katulad sa ilalim ng lupa, na cool din.

sanggunian

  1. Opisyal na dokumentasyon ng TON: https://test.ton.org
  2. Opisyal na imbakan ng TON: https://github.com/ton-blockchain/ton
  3. Opisyal na wallet para sa iba't ibang platform: https://wallet.ton.org
  4. Smart contract repository mula sa artikulong ito: https://github.com/raiym/astonished
  5. Link sa website ng smart contract: https://ton-lottery.appspot.com
  6. Imbakan para sa extension para sa Visual Studio Code para sa FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Makipag-chat tungkol sa TON sa Telegram, na talagang nakatulong upang malaman ito sa paunang yugto. Sa tingin ko, hindi magiging pagkakamali kung sasabihin kong nandoon ang lahat ng sumulat para kay TON. Maaari ka ring humingi ng test grams doon. https://t.me/tondev_ru
  8. Isa pang chat tungkol sa TON kung saan nakakita ako ng kapaki-pakinabang na impormasyon: https://t.me/TONgramDev
  9. Unang yugto ng kumpetisyon: https://contest.com/blockchain
  10. Ikalawang yugto ng kumpetisyon: https://contest.com/blockchain-2

Pinagmulan: www.habr.com

Magdagdag ng komento