Telegram Open Network-da aqlli shartnomani qanday yozish va nashr etish haqida (TON)

TONda aqlli shartnomani qanday yozish va nashr qilish haqida

Ushbu maqola nima haqida?

Maqolada men Telegram blokcheynidagi birinchi (ikkitadan) tanlovida qanday ishtirok etganim, sovrin olmaganim va tajribam unutilib ketmasligi va, ehtimol, yordam berishi uchun maqolada yozib olishga qaror qilganim haqida gapiraman. kimdir.

Men mavhum kod yozishni emas, balki biron bir ish bilan shug'ullanishni istamaganim uchun, maqola uchun men lahzali lotereya uchun aqlli shartnoma va oraliq xotiradan foydalanmasdan to'g'ridan-to'g'ri TON-dan aqlli shartnoma ma'lumotlarini ko'rsatadigan veb-sayt yozdim.

Maqola TONda birinchi aqlli shartnomasini tuzmoqchi bo'lganlar uchun foydali bo'ladi, lekin qaerdan boshlashni bilmaydi.

Lotereyani misol sifatida ishlatib, men muhitni o'rnatishdan aqlli shartnomani nashr qilish, u bilan o'zaro aloqa qilish va ma'lumotlarni qabul qilish va nashr etish uchun veb-sayt yozishga o'taman.

Tanlovda ishtirok etish haqida

Oβ€˜tgan yilning oktyabr oyida Telegram yangi tillar bilan blokcheyn tanlovini e’lon qildi Fift ΠΈ FunC. Taklif etilgan beshta aqlli shartnomadan birini yozishni tanlash kerak edi. Kelajakda boshqa hech narsa yozish shart boβ€˜lmasa ham, boshqacha ish qilsam, til oβ€˜rgansam, nimadir qilsam yaxshi boβ€˜lardi, deb oβ€˜yladim. Bundan tashqari, mavzu doimo labda.

Aytishim kerakki, men aqlli shartnomalarni ishlab chiqishda tajribam yo'q edi.

Men imkon qadar oxirigacha qatnashishni va keyin sharh maqola yozishni rejalashtirgandim, lekin birinchisida darhol muvaffaqiyatsizlikka uchraganman. I hamyon yozgan ko'p imzo yoqilgan FunC va u umuman ishladi. Men buni asos qilib oldim Solidity bo'yicha aqlli shartnoma.

O'shanda men bu hech bo'lmaganda sovrinli o'rinni egallash uchun yetarli, deb o'ylardim. Natijada 40 nafar ishtirokchidan 60 nafarga yaqini sovrindor boβ€˜ldi va men ular orasida yoβ€˜q edim. Umuman olganda, buning hech qanday yomon joyi yo'q, lekin meni bir narsa bezovta qildi. Natijalar e'lon qilingan paytda, mening shartnomam bo'yicha testni ko'rib chiqish o'tkazilmagan edi, men suhbat ishtirokchilaridan boshqa hech kim yo'qligini so'radim, yo'q edi.

Mening xabarlarimga e'tibor bergan shekilli, ikki kundan keyin hakamlar izoh e'lon qilishdi va men hali ham tushunmayapman, ular hakamlik paytida tasodifan mening aqlli shartnomamni o'tkazib yubordilarmi yoki shunchaki buni juda yomon deb o'ylashdimi, izohga muhtoj emasman. Men sahifada savol berdim, lekin javob olmadim. Kim hukm qilgani sir bo'lmasa-da, shaxsiy xabarlar yozishni keraksiz deb bildim.

Tushunishga ko'p vaqt sarflandi, shuning uchun maqola yozishga qaror qilindi. Hali ko'p ma'lumot yo'qligi sababli, ushbu maqola qiziqqan har bir kishi uchun vaqtni tejashga yordam beradi.

TONdagi aqlli shartnomalar kontseptsiyasi

Biror narsa yozishdan oldin, bu narsaga qaysi tomondan yondashish kerakligini aniqlab olishingiz kerak. Shuning uchun, endi men sizga tizim qanday qismlardan iboratligini aytib beraman. Aniqrog'i, hech bo'lmaganda qandaydir mehnat shartnomasini yozish uchun qanday qismlarni bilishingiz kerak.

Biz aqlli shartnoma yozish va u bilan ishlashga e'tibor qaratamiz TON Virtual Machine (TVM), Fift ΠΈ FunC, shuning uchun maqola ko'proq muntazam dasturni ishlab chiqish tavsifiga o'xshaydi. Biz bu erda platformaning o'zi qanday ishlashi haqida to'xtalmaymiz.

Umuman olganda, u qanday ishlashi haqida TVM va til Fift yaxshi rasmiy hujjatlar mavjud. Tanlovda qatnashayotganimda va hozirgi shartnomani yozishda men unga tez-tez murojaat qilardim.

Aqlli shartnomalar yoziladigan asosiy til FunC. Hozirda bu borada hech qanday hujjat yo'q, shuning uchun biror narsa yozish uchun siz rasmiy ombordan aqlli shartnomalar misollarini va u erda tilning o'zini amalga oshirishni o'rganishingiz kerak, shuningdek, so'nggi ikki yildagi aqlli shartnomalar misollarini ko'rishingiz mumkin. musobaqalar. Maqolaning oxiridagi havolalar.

Aytaylik, biz allaqachon aqlli shartnoma yozdik FunC, shundan so'ng biz kodni Fift assemblerga kompilyatsiya qilamiz.

Tuzilgan aqlli shartnoma hali ham nashr etilishi kerak. Buni amalga oshirish uchun siz funktsiyani yozishingiz kerak Fift, kirish sifatida aqlli shartnoma kodini va boshqa parametrlarni oladi va chiqish kengaytmali fayl bo'ladi. .boc (bu "hujayralar sumkasi" degan ma'noni anglatadi) va uni qanday yozishimizga qarab, aqlli shartnoma kodi asosida yaratilgan shaxsiy kalit va manzil. Siz allaqachon e'lon qilinmagan aqlli shartnoma manziliga gramm yuborishingiz mumkin.

TONda aqlli shartnomani nashr qilish uchun qabul qilindi .boc faylni blokcheynga engil mijoz yordamida yuborish kerak bo'ladi (quyida batafsilroq). Ammo nashr etishdan oldin siz grammlarni yaratilgan manzilga o'tkazishingiz kerak, aks holda aqlli shartnoma nashr etilmaydi. Nashr etilgandan so'ng, siz aqlli shartnoma bilan tashqi tomondan (masalan, engil mijoz yordamida) yoki ichkaridan xabarlar yuborish orqali o'zaro aloqada bo'lishingiz mumkin (masalan, bitta aqlli shartnoma boshqasiga TON ichida xabar yuboradi).

Kod qanday chop etilishini tushunganimizdan so'ng, bu osonroq bo'ladi. Biz taxminan nima yozmoqchi ekanligimizni va dasturimiz qanday ishlashini bilamiz. Yozish paytida biz bu mavjud aqlli shartnomalarda qanday amalga oshirilganligini qidiramiz yoki amalga oshirish kodini ko'rib chiqamiz Fift ΠΈ FunC rasmiy omborda yoki rasmiy hujjatlarga qarang.

Men tez-tez barcha tanlov ishtirokchilari va Telegram xodimlari to'plangan Telegram chatida kalit so'zlarni qidirardim va shunday bo'ldiki, musobaqa davomida hamma u erga yig'ilib, Fift va FunCni muhokama qila boshladi. Maqolaning oxiridagi havola.

Nazariyadan amaliyotga o'tish vaqti keldi.

TON bilan ishlash uchun muhitni tayyorlash

Men MacOS-dagi maqolada tasvirlangan hamma narsani qildim va uni Docker-dagi toza Ubuntu 18.04 LTS da ikki marta tekshirdim.

Siz qilishingiz kerak bo'lgan birinchi narsa - yuklab olish va o'rnatish lite-client uning yordamida siz TONga so'rov yuborishingiz mumkin.

Rasmiy veb-saytdagi ko'rsatmalar o'rnatish jarayonini juda batafsil va aniq tasvirlab beradi va ba'zi tafsilotlarni o'tkazib yuboradi. Bu erda biz ko'rsatmalarga amal qilamiz, yo'l davomida etishmayotgan bog'liqliklarni o'rnatamiz. Men har bir loyihani o'zim kompilyatsiya qilmadim va rasmiy Ubuntu omboridan o'rnatmadim (men MacOS da ishlatganman 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 

Barcha bog'liqliklar o'rnatilgandan so'ng siz o'rnatishingiz mumkin lite-client, Fift, FunC.

Birinchidan, biz TON omborini uning bog'liqliklari bilan birga klonlaymiz. Qulaylik uchun biz hamma narsani papkada qilamiz ~/TON.

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

Repository shuningdek, ilovalarni saqlaydi Fift ΠΈ FunC.

Endi biz loyihani yig'ishga tayyormiz. Ombor kodi papkaga klonlanadi ~/TON/ton. The ~/TON papka yarating build va unda loyihani to'plang.

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

Biz aqlli shartnoma yozmoqchi bo'lganimiz uchun, biz nafaqat kerak lite-clientlekin Fift с FunC, shuning uchun hamma narsani kompilyatsiya qilaylik. Bu tez jarayon emas, shuning uchun biz kutamiz.

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

Keyin, qaysi tugun haqida ma'lumotni o'z ichiga olgan konfiguratsiya faylini yuklab oling lite-client ulanadi.

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

TONga birinchi so'rovlarni qilish

Endi ishga tushamiz lite-client.

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

Agar qurish muvaffaqiyatli bo'lsa, ishga tushirilgandan so'ng siz yorug'lik mijozining tugunga ulanishi jurnalini ko'rasiz.

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

Siz buyruqni bajarishingiz mumkin help va qanday buyruqlar mavjudligini ko'ring.

help

Keling, ushbu maqolada foydalanadigan buyruqlarni sanab o'tamiz.

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-ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ смартконтракта. 

Endi biz shartnomani o'zi yozishga tayyormiz.

РСализация

Fikr

Yuqorida yozganimdek, biz yozayotgan aqlli shartnoma lotereyadir.

Bundan tashqari, bu siz chipta sotib olishingiz va bir soat, kun yoki oy kutishingiz kerak bo'lgan lotereya emas, balki foydalanuvchi shartnoma manziliga o'tkazadigan lahzali lotereyadir. N gramm va uni darhol qaytarib oladi 2 * N gramm yoki yo'qotadi. Biz g'alaba qozonish ehtimolini taxminan 40% qilamiz. Agar to'lov uchun gramm etarli bo'lmasa, biz tranzaktsiyani to'ldirish sifatida ko'rib chiqamiz.

Bundan tashqari, garovlarni real vaqt rejimida va qulay shaklda ko'rish mumkin bo'lishi muhim, shunda foydalanuvchi o'zining yutgan yoki yutqazganligini darhol tushunishi mumkin. Shuning uchun, siz tikish va natijalarni to'g'ridan-to'g'ri TONdan ko'rsatadigan veb-sayt qilishingiz kerak.

Aqlli shartnoma yozish

Qulaylik uchun men FunC kodini ajratib ko'rsatdim; plaginni Visual Studio Code qidiruvida topish va o'rnatish mumkin; agar siz to'satdan biror narsa qo'shmoqchi bo'lsangiz, men plaginni hamma uchun ochiq qildim. Bundan tashqari, kimdir ilgari Fift bilan ishlash uchun plagin yaratgan, siz uni o'rnatishingiz va VSC-da topishingiz mumkin.

Keling, darhol oraliq natijalarni beradigan omborni yarataylik.

Hayotimizni osonlashtirish uchun biz aqlli shartnoma yozamiz va tayyor bo'lgunga qadar uni mahalliy sinovdan o'tkazamiz. Shundan keyingina biz uni TONda nashr qilamiz.

Aqlli kontraktda kirish mumkin bo'lgan ikkita tashqi usul mavjud. Birinchidan, recv_external() bu funksiya shartnomaga so'rov tashqi dunyodan kelganda, ya'ni TONdan emas, masalan, biz o'zimiz xabar ishlab chiqarganimizda va uni lite-mijoz orqali jo'natganimizda bajariladi. Ikkinchi, recv_internal() Bu, TONning o'zida, har qanday shartnoma biznikiga tegishli. Ikkala holatda ham siz funksiyaga parametrlarni o'tkazishingiz mumkin.

Keling, oddiy misol bilan boshlaylik, agar nashr etilsa ishlaydi, lekin unda funktsional yuk yo'q.

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

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

Bu erda biz nima ekanligini tushuntirishimiz kerak slice. TON Blockchain-da saqlangan barcha ma'lumotlar to'plamdir TVM cell yoki oddiygina cell, bunday katakda siz 1023 bitgacha ma'lumotni va boshqa hujayralarga 4 tagacha havolani saqlashingiz mumkin.

TVM cell slice yoki slice bu mavjud qismning bir qismi cell uni tahlil qilish uchun ishlatiladi, keyinroq aniq bo'ladi. Biz uchun asosiysi transfer qilishimiz mumkin slice va xabar turiga qarab, ma'lumotlarni qayta ishlang recv_external() yoki recv_internal().

impure β€” funksiya aqlli shartnoma maΚΌlumotlarini oΚ»zgartirishini bildiruvchi kalit soΚ»z.

Keling, shartnoma kodini saqlaymiz lottery-code.fc va kompilyatsiya qilish.

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

Bayroqlarning ma'nosini buyruq yordamida ko'rish mumkin

~/TON/build/crypto/func -help

Biz Fift assembler kodini tuzdik 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

Uni mahalliy darajada ishga tushirish mumkin, buning uchun biz muhitni tayyorlaymiz.

E'tibor bering, birinchi qator ulanadi Asm.fif, bu Fift assembler uchun Fiftda yozilgan kod.

Biz aqlli shartnomani mahalliy sifatida ishga tushirish va sinab ko'rishni xohlayotganimiz sababli, biz fayl yaratamiz lottery-test-suite.fif va tuzilgan kodni u erda nusxa ko'chiring, undagi oxirgi qatorni almashtiring, bu aqlli shartnoma kodini doimiyga yozadi. codekeyin uni virtual mashinaga o'tkazish uchun:

"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

Hozircha bu aniq ko'rinadi, endi TVMni ishga tushirish uchun foydalanadigan kodni o'sha faylga qo'shamiz.

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 biz kontekstni, ya'ni TVM (yoki tarmoq holati) ishga tushiriladigan ma'lumotlarni yozamiz. Hatto tanlov davomida ishlab chiquvchilardan biri qanday yaratishni ko'rsatdi c7 va men nusxa ko'chirdim. Ushbu maqolada biz o'zgartirishimiz kerak bo'lishi mumkin rand_seed chunki tasodifiy sonni yaratish unga bog'liq va agar o'zgartirilmasa, har safar bir xil raqam qaytariladi.

recv_internal ΠΈ recv_external 0 va -1 qiymatlari bo'lgan konstantalar aqlli shartnomada mos keladigan funktsiyalarni chaqirish uchun javobgar bo'ladi.

Endi biz bo'sh aqlli shartnomamiz uchun birinchi testni yaratishga tayyormiz. Aniqlik uchun hozircha biz barcha testlarni bitta faylga qo'shamiz lottery-test-suite.fif.

Keling, o'zgaruvchini yarataylik storage va unga bo'sh birini yozing cell, bu aqlli kontrakt xotirasi bo'ladi.

message bu tashqi tomondan aqlli kontaktga uzatadigan xabar. Biz uni hozircha bo'sh qilamiz.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Konstantalar va o'zgaruvchilarni tayyorlaganimizdan so'ng, biz buyruq yordamida TVM ni ishga tushiramiz runvmctx va yaratilgan parametrlarni kiritishga o'tkazing.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Oxir-oqibat biz muvaffaqiyatga erishamiz bu uchun oraliq kod Fift.

Endi biz olingan kodni ishga tushirishimiz mumkin.

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

Dastur xatosiz ishlashi kerak va natijada biz ijro jurnalini ko'ramiz:

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

Ajoyib, biz aqlli shartnomaning birinchi ishchi versiyasini yozdik.

Endi biz funksionallikni qo'shishimiz kerak. Avval tashqi dunyodan kelgan xabarlar bilan shug'ullanamiz recv_external()

Ishlab chiquvchining o'zi shartnoma qabul qilishi mumkin bo'lgan xabar formatini tanlaydi.

Lekin odatda

  • birinchidan, biz shartnomamizni tashqi dunyodan himoya qilmoqchimiz va uni faqat shartnoma egasi unga tashqi xabarlarni yuborishi mumkin bo'lgan qilib yaratmoqchimiz.
  • ikkinchidan, biz TON ga to'g'ri xabar yuborganimizda, biz buni aynan bir marta sodir bo'lishini istaymiz va biz yana bir xil xabarni yuborganimizda, aqlli shartnoma uni rad etadi.

Shunday qilib, deyarli har bir shartnoma bu ikki muammoni hal qiladi, chunki bizning shartnomamiz tashqi xabarlarni qabul qiladi, biz bu haqda ham g'amxo'rlik qilishimiz kerak.

Biz buni teskari tartibda qilamiz. Birinchidan, muammoni takrorlash bilan hal qilaylik, agar shartnoma allaqachon bunday xabarni olgan va uni qayta ishlagan bo'lsa, u ikkinchi marta bajarmaydi. Va keyin biz muammoni faqat ma'lum bir odamlar doirasi aqlli shartnomaga xabar yuborishi uchun hal qilamiz.

Ikki nusxadagi xabarlar bilan muammoni hal qilishning turli usullari mavjud. Buni qanday qilamiz. Aqlli shartnomada biz qabul qilingan xabarlar hisoblagichini boshlang'ich qiymati 0 bilan ishga tushiramiz. Aqlli shartnomaga har bir xabarda joriy hisoblagich qiymatini qo'shamiz. Agar xabardagi hisoblagich qiymati aqlli shartnomadagi qiymatga mos kelmasa, biz uni qayta ishlamaymiz, agar shunday bo'lsa, biz uni qayta ishlaymiz va aqlli kontraktdagi hisoblagichni 1 ga oshiramiz.

ga qaytaylik lottery-test-suite.fif va unga ikkinchi test qo'shing. Agar biz noto'g'ri raqam yuborsak, kod istisno qilishi kerak. Masalan, shartnoma ma'lumotlari 166 ni saqlasin, biz esa 165 ni yuboramiz.

<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"

Keling, ishga tushamiz.

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

Va test xato bilan bajarilganligini ko'ramiz.

[ 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

Bu bosqichda lottery-test-suite.fif kabi ko'rinishi kerak aloqa.

Endi aqlli kontraktga qarshi mantiqni qo'shamiz 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 biz yuborgan xabar yotadi.

Biz qiladigan birinchi narsa - xabarda ma'lumotlar mavjudligini tekshirish, agar bo'lmasa, biz shunchaki chiqamiz.

Keyin biz xabarni tahlil qilamiz. in_msg~load_uint(32) 165, 32 bitli raqamni yuklaydi unsigned int uzatilgan xabardan.

Keyinchalik biz aqlli kontrakt xotirasidan 32 bitni yuklaymiz. Yuklangan raqam o'tgan raqamga mos kelishini tekshiramiz, agar bo'lmasa, biz istisno qilamiz. Bizning holatda, biz o'yindan tashqari o'tayotganimiz sababli, istisno qilish kerak.

Endi kompilyatsiya qilaylik.

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

Olingan koddan nusxa oling lottery-test-suite.fif, oxirgi qatorni almashtirishni unutmang.

Sinovdan o'tganligini tekshiramiz:

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

Bu erda Joriy natijalar bilan tegishli majburiyatni ko'rishingiz mumkin.

E'tibor bering, aqlli shartnomaning tuzilgan kodini doimiy ravishda sinovlar bilan faylga nusxalash noqulay, shuning uchun biz kodni o'zimiz uchun doimiyga yozadigan skript yozamiz va biz kompilyatsiya qilingan kodni oddiygina testlarimizga ulaymiz. "include".

Loyiha papkasida fayl yarating build.sh quyidagi tarkib bilan.

#!/bin/bash

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

Keling, uni bajariladigan holga keltiramiz.

chmod +x ./build.sh

Endi shartnomani tuzish uchun skriptimizni ishga tushiring. Ammo bundan tashqari, biz uni doimiyga yozishimiz kerak code. Shunday qilib, biz yangi fayl yaratamiz lotter-compiled-for-test.fif, biz faylga kiritamiz lottery-test-suite.fif.

Keling, sh ga skript kodini qo'shamiz, bu shunchaki kompilyatsiya qilingan faylni takrorlaydi lotter-compiled-for-test.fif va undagi oxirgi qatorni o'zgartiring.

# 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

Endi tekshirish uchun natijada olingan skriptni ishga tushiramiz va fayl yaratiladi lottery-compiled-for-test.fif, biz buni o'zimizga kiritamiz lottery-test-suite.fif

Π’ lottery-test-suite.fif shartnoma kodini o'chiring va qatorni qo'shing "lottery-compiled-for-test.fif" include.

Biz ularning muvaffaqiyatli ekanligini tekshirish uchun testlarni o'tkazamiz.

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

Ajoyib, endi testlarni ishga tushirishni avtomatlashtirish uchun fayl yarataylik test.sh, bu birinchi navbatda amalga oshiriladi build.sh, va keyin testlarni bajaring.

touch test.sh
chmod +x test.sh

Biz ichkariga yozamiz

./build.sh 

echo "nCompilation completedn"

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

Keling buni bajaramiz test.sh va testlar ishlayotganiga ishonch hosil qilish uchun uni ishga tushiring.

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

Biz shartnoma tuzilganligini va sinovlar o'tkazilganligini tekshiramiz.

Ajoyib, endi ishga tushirildi test.sh Testlar darhol tuziladi va o'tkaziladi. Mana havola topshirmoq.

Mayli, davom etishdan oldin, qulaylik uchun yana bir narsani qilaylik.

Keling, papka yarataylik build bu erda biz ko'chirilgan kontraktni va uning doimiyga yozilgan klonini saqlaymiz lottery-compiled.fif, lottery-compiled-for-test.fif. Keling, papkani ham yarataylik test test fayli qayerda saqlanadi? lottery-test-suite.fif va potentsial boshqa qo'llab-quvvatlovchi fayllar. Tegishli o'zgarishlarga havola.

Keling, aqlli shartnomani ishlab chiqishda davom etaylik.

Keyinchalik, biz to'g'ri raqamni yuborganimizda, xabar qabul qilinganligini va hisoblagich do'konda yangilanganligini tekshiradigan test bo'lishi kerak. Lekin buni keyinroq qilamiz.

Keling, aqlli shartnomada qanday ma'lumotlar tuzilishi va qanday ma'lumotlarni saqlash kerakligini o'ylab ko'raylik.

Biz saqlaydigan hamma narsani tasvirlab beraman.

`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` пСрСмСнная Ρ‚ΠΈΠΏΠ° ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ, Ρ…Ρ€Π°Π½ΠΈΡ‚ послСдниС Π΄Π²Π°Π΄Ρ†Π°Ρ‚ΡŒ ставок. 

Keyin ikkita funktsiyani yozishingiz kerak. Birinchisiga qo'ng'iroq qilaylik pack_state(), bu keyinchalik aqlli kontrakt xotirasida saqlash uchun ma'lumotlarni to'playdi. Keling, ikkinchisiga qo'ng'iroq qilaylik unpack_state() xotiradan ma'lumotlarni o'qiydi va qaytaradi.

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

Biz ushbu ikkita funktsiyani aqlli shartnomaning boshiga qo'shamiz. Bu amalga oshadi bu oraliq natija.

Ma'lumotlarni saqlash uchun siz o'rnatilgan funksiyani chaqirishingiz kerak bo'ladi set_data() va u ma'lumotlarni yozadi pack_state() aqlli kontrakt xotirasida.

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

Endi bizda ma'lumotlarni yozish va o'qish uchun qulay funktsiyalar mavjud, biz davom etishimiz mumkin.

Biz tashqaridan kelgan xabarni shartnoma egasi (yoki shaxsiy kalitga kirish huquqiga ega bo'lgan boshqa foydalanuvchi) imzolaganligini tekshirishimiz kerak.

Biz aqlli shartnomani nashr qilganimizda, biz uni saqlashda kerak bo'lgan ma'lumotlar bilan ishga tushirishimiz mumkin, ular kelajakda foydalanish uchun saqlanadi. Kiruvchi xabar tegishli shaxsiy kalit bilan imzolanganligini tekshirishimiz uchun biz u yerda ochiq kalitni yozib olamiz.

Davom etishdan oldin shaxsiy kalit yaratamiz va uni yozamiz test/keys/owner.pk. Buning uchun interaktiv rejimda Fiftni ishga tushiramiz va to'rtta buyruqni bajaramiz.

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

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

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

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

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

Keling, papka yarataylik keys papka ichida test va u erda shaxsiy kalitni yozing.

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

Biz joriy papkada faylni ko'ramiz owner.pk.

Biz ochiq kalitni stekdan olib tashlaymiz va kerak bo'lganda uni shaxsiy kalitdan olishimiz mumkin.

Endi biz imzo tekshiruvini yozishimiz kerak. Sinovdan boshlaylik. Avval biz funktsiyadan foydalanib fayldan shaxsiy kalitni o'qiymiz file>B va uni o'zgaruvchiga yozing owner_private_key, keyin funksiyadan foydalaning priv>pub shaxsiy kalitni umumiy kalitga aylantiring va natijani yozing 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 !

Bizga ikkala kalit ham kerak bo'ladi.

Biz aqlli kontrakt xotirasini funktsiyadagi kabi ketma-ketlikda o'zboshimchalik bilan ma'lumotlar bilan ishga tushiramiz pack_state()va uni o'zgaruvchiga yozing 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 !

Keyinchalik, biz imzolangan xabarni tuzamiz, unda faqat imzo va hisoblagich qiymati bo'ladi.

Birinchidan, biz uzatmoqchi bo'lgan ma'lumotlarni yaratamiz, keyin uni shaxsiy kalit bilan imzolaymiz va nihoyat imzolangan xabarni yaratamiz.

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 !  

Natijada, biz aqlli shartnomaga yuboradigan xabar o'zgaruvchida qayd etiladi message_to_send, funksiyalar haqida hashu, ed25519_sign_uint o'qishingiz mumkin Fift hujjatlarida.

Va testni o'tkazish uchun biz yana qo'ng'iroq qilamiz.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Mana bunday Sinovlar bilan fayl ushbu bosqichda shunday ko'rinishi kerak.

Keling, sinovni o'tkazamiz va u muvaffaqiyatsiz bo'ladi, shuning uchun biz aqlli shartnomani ushbu formatdagi xabarlarni qabul qilishi va imzoni tekshirishi uchun o'zgartiramiz.

Birinchidan, biz xabardan imzoning 512 bitini hisoblaymiz va uni o'zgaruvchiga yozamiz, keyin hisoblagich o'zgaruvchining 32 bitini hisoblaymiz.

Bizda aqlli kontrakt xotirasidan ma'lumotlarni o'qish funktsiyasi mavjud bo'lgani uchun biz undan foydalanamiz.

Keyingi - saqlash bilan uzatilgan hisoblagichni tekshirish va imzoni tekshirish. Agar biror narsa mos kelmasa, biz tegishli kod bilan istisno qilamiz.

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

Tegishli majburiyat Bu erda.

Keling, testlarni o'tkazamiz va ikkinchi sinov muvaffaqiyatsiz ekanligini ko'ramiz. Ikkita sababga ko'ra, xabarda bitlar etarli emas va xotirada etarli bitlar mavjud emas, shuning uchun kod tahlil qilishda ishlamay qoladi. Biz yuborayotgan xabarga imzo qo'shishimiz va oxirgi sinovdan saqlashni nusxalashimiz kerak.

Ikkinchi testda biz xabar imzosini qo'shamiz va aqlli kontrakt xotirasini o'zgartiramiz. Mana bunday sinovlari bo'lgan fayl hozirgidek ko'rinadi.

Keling, to'rtinchi testni yozaylik, unda biz boshqa birovning shaxsiy kaliti bilan imzolangan xabarni yuboramiz. Keling, boshqa shaxsiy kalit yaratamiz va uni faylga saqlaymiz not-owner.pk. Biz ushbu shaxsiy kalit bilan xabarni imzolaymiz. Keling, testlarni o'tkazamiz va barcha testlar o'tganiga ishonch hosil qilamiz. Qabul qiling shu daqiqada.

Endi biz nihoyat aqlli kontrakt mantig'ini amalga oshirishga o'tishimiz mumkin.
Π’ recv_external() biz ikki turdagi xabarlarni qabul qilamiz.

Shartnomamiz o'yinchilarning yo'qotishlarini jamlaganligi sababli, bu pul lotereya yaratuvchisiga o'tkazilishi kerak. Lotereya yaratuvchisining hamyon manzili shartnoma tuzilganda saqlashda qayd etiladi.

Har holda, biz yutqazganlarning grammlarini yuboradigan manzilni o'zgartirish qobiliyatiga muhtojmiz. Shuningdek, lotereyadan egasining manziliga gramm yuborishimiz kerak.

Birinchisidan boshlaylik. Avval xabarni yuborganingizdan so'ng, aqlli shartnoma yangi manzilni xotirada saqlaganligini tekshiradigan test yozamiz. Xabarda hisoblagich va yangi manzildan tashqari biz ham uzatamiz action 7 bitli butun son manfiy bo'lmagan raqam, unga qarab, biz aqlli shartnomada xabarni qanday qayta ishlashni tanlaymiz.

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

Sinovda siz aqlli kontrakt xotirasi qanday seriyadan chiqarilganligini ko'rishingiz mumkin storage beshda. O'zgaruvchilarning seriyasizlanishi Fift hujjatlarida tasvirlangan.

Havolani tasdiqlang xamir qo'shilishi bilan.

Keling, sinovni o'tkazamiz va uning muvaffaqiyatsizligiga ishonch hosil qilamiz. Endi lotereya egasining manzilini o'zgartirish uchun mantiq qo'shamiz.

Aqlli shartnomada biz tahlil qilishni davom ettiramiz message, o'qing action. Eslatib o'tamiz, bizda ikkita bo'ladi action: manzilni o'zgartiring va gramm yuboring.

Keyin shartnoma egasining yangi manzilini o'qiymiz va uni saqlash joyiga saqlaymiz.
Biz testlarni o'tkazamiz va uchinchi sinov muvaffaqiyatsiz ekanligini ko'ramiz. Shartnoma endi sinovda etishmayotgan xabarning 7 bitini qo'shimcha ravishda tahlil qilganligi sababli u buziladi. Xabarga mavjud bo'lmaganni qo'shing action. Keling, testlarni o'tkazamiz va hamma narsa o'tishini ko'ramiz. shu yerda o'zgarishlarga rozi bo'ling. Ajoyib.

Endi belgilangan miqdordagi grammni avval saqlangan manzilga yuborish mantiqini yozamiz.

Birinchidan, test yozamiz. Biz ikkita test yozamiz, biri muvozanat bo'lmasa, ikkinchisi hamma narsa muvaffaqiyatli o'tishi kerak. Sinovlarni ko'rish mumkin bu majburiyatda.

Endi kodni qo'shamiz. Birinchidan, ikkita yordamchi usulni yozamiz. Birinchi olish usuli aqlli shartnomaning joriy balansini aniqlashdir.

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

Ikkinchisi esa boshqa aqlli shartnomaga gramm yuborish uchun. Men ushbu usulni boshqa aqlli shartnomadan to'liq ko'chirib oldim.

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

Keling, ushbu ikki usulni aqlli shartnomaga qo'shamiz va mantiqni yozamiz. Birinchidan, biz xabardan grammlar sonini tahlil qilamiz. Keyin balansni tekshiramiz, agar bu etarli bo'lmasa, biz istisno qilamiz. Har bir narsa yaxshi bo'lsa, biz grammlarni saqlangan manzilga yuboramiz va hisoblagichni yangilaymiz.

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

Mana bunday hozirgi paytda aqlli shartnomaga o'xshaydi. Keling, testlarni o'tkazamiz va ularning muvaffaqiyatli o'tishiga ishonch hosil qilamiz.

Aytgancha, har safar qayta ishlangan xabar uchun aqlli shartnomadan komissiya yechib olinadi. Smart kontrakt xabarlari so'rovni bajarishi uchun asosiy tekshiruvlardan so'ng siz qo'ng'iroq qilishingiz kerak accept_message().

Endi ichki xabarlarga o'tamiz. Aslida, biz faqat grammlarni qabul qilamiz va agar o'yinchi g'alaba qozonsa, ikki baravar pulni, agar u yutqazsa, uchinchisini egasiga qaytarib yuboramiz.

Birinchidan, oddiy test yozamiz. Buning uchun bizga aqlli kontraktning sinov manzili kerak bo'ladi, undan biz go'yoki aqlli kontraktga gramm yuboramiz.

Aqlli kontrakt manzili ikkita raqamdan iborat bo'lib, ish zanjiri uchun mas'ul bo'lgan 32 bitli tamsayΔ± va ushbu ish zanjiridagi 256 bitli manfiy bo'lmagan butun son noyob hisob raqami. Masalan, -1 va 12345, bu biz faylga saqlaydigan manzil.

Manzilni saqlash funksiyasidan nusxa oldim 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

Funktsiya qanday ishlashini ko'rib chiqaylik, bu Fift qanday ishlashi haqida tushuncha beradi. Interaktiv rejimda Fiftni ishga tushiring.

~/TON/build/crypto/fift -i 

Avval biz stekga -1, 12345 va kelajakdagi fayl nomi "sender.addr" ni bosing:

-1 12345 "sender.addr" 

Keyingi qadam funktsiyani bajarishdir -rot, bu stekni shunday o'zgartiradiki, stekning yuqori qismida noyob aqlli kontrakt raqami mavjud:

"sender.addr" -1 12345

256 u>B 256 bitli manfiy bo'lmagan butun sonni baytga aylantiradi.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap stekning yuqori ikki elementini almashtiradi.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B 32 bitli butun sonni baytga aylantiradi.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ baytlarning ikkita ketma-ketligini bog'laydi.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Yana swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Va nihoyat baytlar faylga yoziladi B>file. Shundan so'ng bizning stekimiz bo'sh. Biz to'xtadik Fift. Joriy jildda fayl yaratildi sender.addr. Keling, faylni yaratilgan papkaga o'tkazamiz test/addresses/.

Keling, aqlli shartnomaga gramm yuboradigan oddiy test yozaylik. Mana, majburiyat.

Endi lotereya mantig'ini ko'rib chiqaylik.

Biz qiladigan birinchi narsa - xabarni tekshirish bounced yoki agar bo'lmasa bounced, keyin biz bunga e'tibor bermaymiz. bounced Agar biron bir xatolik yuzaga kelsa, shartnoma grammlarni qaytaradi, degan ma'noni anglatadi. Agar to'satdan xatolik yuzaga kelsa, biz grammlarni qaytarmaymiz.

Biz tekshiramiz, agar balans yarim grammdan kam bo'lsa, biz shunchaki xabarni qabul qilamiz va unga e'tibor bermaymiz.

Keyinchalik, biz xabar kelgan aqlli shartnoma manzilini tahlil qilamiz.

Biz saqlash ma'lumotlarini o'qiymiz va agar ular yigirmatadan ortiq bo'lsa, eski tikishlarni tarixdan o'chirib tashlaymiz. Qulaylik uchun men uchta qo'shimcha funktsiyani yozdim pack_order(), unpack_order(), remove_old_orders().

Keyinchalik, balans to'lov uchun etarli bo'lmasa, biz bu pul tikish emas, balki to'ldirish deb hisoblaymiz va to'ldirishni saqlab qo'yamiz. orders.

Keyin nihoyat aqlli shartnomaning mohiyati.

Birinchidan, agar o'yinchi yutqazsa, biz uni tikish tarixida saqlaymiz va agar summa 3 grammdan ortiq bo'lsa, biz aqlli shartnoma egasiga 1/3 qismini yuboramiz.

Agar o'yinchi g'alaba qozonsa, biz o'yinchining manziliga ikki baravar pul yuboramiz, so'ngra tikish haqidagi ma'lumotlarni tarixda saqlaymiz.

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

Hammasi shu. Tegishli majburiyat.

Endi qolgan narsa oddiy, keling, shartnoma holati to'g'risida tashqi dunyodan ma'lumot olishimiz uchun olish usullarini yarataylik (aslida ularning aqlli kontrakt xotirasidan ma'lumotlarni o'qing).

Keling, olish usullarini qo'shamiz. Qanday qilib aqlli shartnoma haqida ma'lumot olish haqida quyida yozamiz.

Men aqlli shartnomani nashr qilishda yuzaga keladigan birinchi so'rovni qayta ishlaydigan kodni qo'shishni ham unutib qo'ydim. Tegishli majburiyat. Va yana tuzatilgan summaning 1/3 qismini egasining hisobiga yuborish bilan bog'liq xato.

Keyingi qadam aqlli shartnomani nashr qilishdir. Keling, papka yarataylik requests.

Men nashr kodini asos qilib oldim simple-wallet-code.fc qaysi biri Siz topishingiz mumkin rasmiy omborda.

E'tibor berishga arziydigan narsa. Biz aqlli kontrakt xotirasi va kirish xabarini yaratamiz. Shundan so'ng, smart-kontrakt manzili yaratiladi, ya'ni manzil TONda nashr etilishidan oldin ham ma'lum. Keyinchalik, ushbu manzilga bir necha gramm yuborishingiz kerak va shundan keyingina siz aqlli shartnoma bilan faylni yuborishingiz kerak, chunki tarmoq aqlli shartnoma va undagi operatsiyalarni saqlash uchun komissiya oladi (smartni saqlaydigan va bajaradigan tasdiqlovchilar). shartnomalar). Kodni bu erda ko'rish mumkin.

Keyin nashr kodini bajaramiz va olamiz lottery-query.boc aqlli shartnoma fayli va manzili.

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

Yaratilgan fayllarni saqlashni unutmang: lottery-query.boc, lottery.addr, lottery.pk.

Boshqa narsalar qatorida, biz aqlli shartnoma manzilini ijro jurnallarida ko'ramiz.

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

Faqat o'yin-kulgi uchun, keling, TONga so'rov yuboraylik

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

Va biz ushbu manzil bilan hisob bo'sh ekanligini ko'ramiz.

account state is empty

Manzilga yuboramiz 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram va bir necha soniyadan so'ng biz bir xil buyruqni bajaramiz. Gram yuborish uchun men foydalanaman rasmiy hamyon, va siz suhbatdan kimdirdan test grammlarini so'rashingiz mumkin, men maqolaning oxirida gaplashaman.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Boshlanmaganga o'xshaydi (state:account_uninit) bir xil manzil va 1 000 000 000 nanogramm balansga ega aqlli shartnoma.

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

Endi aqlli shartnomani nashr qilaylik. Keling, lite-clientni ishga tushiramiz va bajaramiz.

> 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 

Keling, shartnoma e'lon qilinganligini tekshirib ko'raylik.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Boshqa narsalar qatorida biz ham olamiz.

  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

Biz buni ko'ramiz account_active.

O'zgarishlarga mos keladigan majburiyat Bu erda.

Keling, aqlli kontrakt bilan ishlash uchun so'rovlarni yarataylik.

Aniqrog'i, birinchisini manzilni o'zgartirish uchun mustaqil ish sifatida qoldiramiz, ikkinchisini esa egasining manziliga gramm yuborish uchun qilamiz. Aslida, biz grammlarni yuborish uchun testda bo'lgani kabi, xuddi shunday qilishimiz kerak bo'ladi.

Bu biz aqlli shartnoma yuboramiz xabar, qaerda msg_seqno 165, action Yuborish uchun 2 va 9.5 gramm.

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

Shaxsiy kalitingiz bilan xabarni imzolashni unutmang lottery.pk, bu aqlli shartnomani yaratishda avval yaratilgan. Mana, tegishli majburiyat.

Get usullaridan foydalangan holda aqlli shartnomadan ma'lumot olish

Keling, aqlli kontrakt olish usullarini qanday ishlatishni ko'rib chiqaylik.

Ishga tushirish lite-client va biz yozgan get usullarini ishga tushiring.

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

Π’ result funksiya qaytaradigan qiymatni o'z ichiga oladi balance() aqlli shartnomamizdan.
Yana bir nechta usullar uchun ham xuddi shunday qilamiz.

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

Keling, tikish tarixingizni so'raymiz.

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

Biz lite-client-dan foydalanamiz va saytdagi aqlli shartnoma haqidagi ma'lumotlarni ko'rsatish usullarini olamiz.

Veb-saytda aqlli shartnoma ma'lumotlarini ko'rsatish

Men aqlli shartnoma ma'lumotlarini qulay tarzda ko'rsatish uchun Python-da oddiy veb-sayt yozdim. Bu erda men bu haqda batafsil to'xtalmayman va saytni nashr etaman bitta majburiyatda.

TONga so'rovlar quyidagilardan amalga oshiriladi Python yordamida lite-client. Qulaylik uchun sayt Docker-da paketlangan va Google Cloud-da nashr etilgan. Havola.

Urinish

Keling, to'ldirish uchun u erga gramm yuborishga harakat qilaylik hamyon. Biz 40 gramm yuboramiz. Va aniqlik uchun bir nechta pul tikamiz. Ko'ramizki, sayt garovlar tarixi, joriy yutuq foizi va boshqa foydali ma'lumotlarni ko'rsatadi.

Ko'ramizBirinchisida g'alaba qozonganimizni, ikkinchisida yutqazganimizni.

So'zdan keyin

Maqola men kutganimdan ancha uzun bo'lib chiqdi, ehtimol u qisqaroq bo'lishi mumkin edi, yoki shunchaki TON haqida hech narsa bilmaydigan va u bilan o'zaro aloqada bo'lish qobiliyatiga ega unchalik oddiy bo'lmagan aqlli shartnoma yozish va nashr etishni xohlaydigan odam uchun. bu. Ehtimol, ba'zi narsalarni soddaroq tushuntirish mumkin edi.

Ehtimol, amalga oshirishning ba'zi jihatlari yanada samarali va oqlangan bo'lishi mumkin edi, ammo keyin maqolani tayyorlash uchun ko'proq vaqt kerak bo'lar edi. Bundan tashqari, men biror joyda xatoga yo'l qo'ygan yoki biror narsani tushunmagan bo'lishim mumkin, shuning uchun agar siz jiddiy ish qilmoqchi bo'lsangiz, rasmiy hujjatlarga yoki TON kodli rasmiy omborga tayanishingiz kerak.

Shuni ta'kidlash kerakki, TONning o'zi hali ham faol rivojlanish bosqichida bo'lganligi sababli, ushbu maqoladagi har qanday qadamni buzadigan o'zgarishlar yuz berishi mumkin (bu men yozayotganimda sodir bo'lgan, u allaqachon tuzatilgan), ammo umumiy yondashuv o'zgarishi dargumon.

Men TON kelajagi haqida gapirmayman. Ehtimol, platforma katta narsaga aylanadi va biz uni o'rganishga vaqt ajratishimiz va mahsulotlarimiz bilan joyni to'ldirishimiz kerak.

TON dan kattaroq foydalanuvchilarning potentsial auditoriyasiga ega Facebook-dan Libra ham mavjud. Men Libra haqida deyarli hech narsa bilmayman, forumga ko'ra, u erda TON hamjamiyatiga qaraganda ko'proq faollik mavjud. TON ishlab chiquvchilari va jamoasi ko'proq yer ostiga o'xshasa ham, bu ham ajoyib.

Manbalar

  1. Rasmiy TON hujjatlari: https://test.ton.org
  2. Rasmiy TON ombori: https://github.com/ton-blockchain/ton
  3. Turli platformalar uchun rasmiy hamyon: https://wallet.ton.org
  4. Ushbu maqoladagi aqlli kontrakt ombori: https://github.com/raiym/astonished
  5. Smart kontrakt veb-saytiga havola: https://ton-lottery.appspot.com
  6. FunC uchun Visual Studio Code kengaytmasi ombori: https://github.com/raiym/func-visual-studio-plugin
  7. Telegramda TON haqida suhbatlashing, bu haqiqatan ham uni dastlabki bosqichda aniqlashga yordam berdi. Menimcha, TON uchun nimadir yozganlarning hammasi o'sha yerda desam xato bo'lmaydi. Siz u erda test grammlarini ham so'rashingiz mumkin. https://t.me/tondev_ru
  8. TON haqida yana bir suhbat, unda men foydali ma'lumotlarni topdim: https://t.me/TONgramDev
  9. Musobaqaning birinchi bosqichi: https://contest.com/blockchain
  10. Tanlovning ikkinchi bosqichi: https://contest.com/blockchain-2

Manba: www.habr.com

a Izoh qo'shish