Telegram Open Network (TON) дээр ухаалаг гэрээг хэрхэн бичиж, нийтлэх талаар

ТОН хэл дээр ухаалаг гэрээг хэрхэн бичих, нийтлэх талаар

Энэ нийтлэл юуны тухай вэ?

Нийтлэлд би Telegram блокчэйний анхны (хоёрын) тэмцээнд хэрхэн оролцож, шагнал аваагүй, мартагдахгүйн тулд өөрийн туршлагаа нийтлэлд бичихээр шийдсэн тухай ярих болно, магадгүй энэ нь туслах болно. хэн нэгэн.

Би хийсвэр код бичихийг хүсээгүй, харин ямар нэг зүйл хийхийг хүссэн тул нийтлэлд зориулж шуурхай сугалааны ухаалаг гэрээ, завсрын санах ой ашиглахгүйгээр TON-оос ухаалаг гэрээний өгөгдлийг шууд харуулдаг вэбсайтыг бичсэн.

Энэхүү нийтлэл нь TON-д анхны ухаалаг гэрээгээ хийхийг хүсч байгаа боловч хаанаас эхлэхээ мэдэхгүй байгаа хүмүүст хэрэгтэй болно.

Сугалааг жишээ болгон ашиглавал би орчинг суулгахаас эхлээд ухаалаг гэрээ нийтлэх, түүнтэй харилцах, мэдээлэл хүлээн авах, нийтлэх вэб сайт бичих хүртэл явна.

Тэмцээнд оролцох тухай

Өнгөрсөн аравдугаар сард Telegram шинэ хэлээр блокчейн уралдаан зарласан Fift и FunC. Санал болгож буй таван ухаалаг гэрээнээс аль нэгийг нь сонгох шаардлагатай байв. Цаашид өөр юм бичихгүй ч гэсэн өөр юм хийж, хэл сурж, юм хийвэл зүгээр юм байна гэж бодсон. Дээрээс нь энэ сэдэв байнга аманд байдаг.

Би ухаалаг гэрээ боловсруулах туршлагагүй гэдгийг хэлэх нь зүйтэй болов уу.

Би эцсээ хүртэл оролцож, дараа нь шүүмжийн нийтлэл бичихээр төлөвлөж байсан ч эхнийх нь шууд бүтэлгүйтсэн. I түрийвч бичсэн олон гарын үсэг асаалттай FunC мөн энэ нь ерөнхийдөө ажилласан. Би үүнийг үндэс болгон авсан Solidity дээрх ухаалаг гэрээ.

Энэ нь ядаж л шагналын байр авахад хангалттай гэж тэр үед бодож байсан. Ингэснээр 40 гаруй оролцогчоос 60 орчим нь шагналт байрт шалгарч, би тэдний дунд байгаагүй. Ерөнхийдөө үүнд буруу зүйл байхгүй, гэхдээ нэг зүйл намайг зовоосон. Үр дүнг зарлах үед миний гэрээний шалгалтын шалгалт хийгээгүй байсан, би чатанд оролцогчдоос өөр хүн байгаа эсэхийг асуусан, байхгүй байсан.

Миний мессежүүдэд анхаарлаа хандуулсан бололтой, хоёр хоногийн дараа шүүгчид сэтгэгдэл нийтэлсэн бөгөөд тэд шүүлтийн үеэр миний ухаалаг гэрээг санамсаргүйгээр алдсан уу, эсвэл зүгээр л тайлбар хийх шаардлагагүй гэж бодсон уу гэдгийг ойлгохгүй байна. Би хуудаснаас асуулт асуусан боловч хариулт аваагүй. Хэдийгээр хэн шүүсэн нь нууц биш ч би хувийн мессеж бичих шаардлагагүй гэж үзсэн.

Ойлгоход багагүй хугацаа зарцуулсан тул нийтлэл бичихээр шийдлээ. Одоогоор тийм ч их мэдээлэл байхгүй байгаа тул энэ нийтлэл нь сонирхсон бүх хүмүүст цаг хэмнэхэд тусална.

ТОН дахь ухаалаг гэрээний тухай ойлголт

Та ямар нэгэн зүйл бичихээсээ өмнө энэ зүйлд аль талаас нь хандахаа олж мэдэх хэрэгтэй. Тиймээс одоо би систем нь ямар хэсгүүдээс бүрдэхийг танд хэлэх болно. Илүү нарийн яривал ядаж ямар нэгэн хөдөлмөрийн гэрээ бичихийн тулд ямар хэсгүүдийг мэдэх шаардлагатай вэ.

Ухаалаг гэрээ бичиж, хамтран ажиллах тал дээр анхаарч ажиллана TON Virtual Machine (TVM), Fift и FunC, тиймээс нийтлэл нь ердийн хөтөлбөрийг хөгжүүлэх тайлбартай илүү төстэй юм. Платформ өөрөө хэрхэн ажилладаг талаар бид энд ярихгүй.

Ерөнхийдөө энэ нь хэрхэн ажилладаг талаар TVM болон хэл Fift сайн албан ёсны бичиг баримт байгаа. Тэмцээнд оролцож байхдаа, одоо гэрээгээ бичиж байхдаа би түүнд байнга ханддаг байсан.

Ухаалаг гэрээг бичдэг гол хэл нь FunC. Одоогоор энэ талаар баримт бичиг байхгүй тул ямар нэгэн зүйл бичихийн тулд та албан ёсны репозитороос ухаалаг гэрээний жишээ, тэнд байгаа хэлний хэрэгжилтийг судлах хэрэгтэй, мөн та сүүлийн хоёр дахь ухаалаг гэрээний жишээг үзэж болно. тэмцээнүүд. Өгүүллийн төгсгөлд байгаа холбоосууд.

Бид аль хэдийн ухаалаг гэрээг бичсэн гэж бодъё FunC, үүний дараа бид кодыг Fift ассемблер руу хөрвүүлдэг.

Эмхэтгэсэн ухаалаг гэрээг нийтлэх хэвээр байна. Үүнийг хийхийн тулд та функц бичих хэрэгтэй Fift, энэ нь ухаалаг гэрээний код болон бусад зарим параметрүүдийг оролт болгон авах ба гаралт нь өргөтгөлтэй файл байх болно. .boc (энэ нь "нүдний уут" гэсэн утгатай) бөгөөд үүнийг хэрхэн бичихээс хамааран ухаалаг гэрээний код дээр үндэслэн үүсгэсэн хувийн түлхүүр, хаяг. Та аль хэдийн хэвлэгдээгүй байгаа ухаалаг гэрээний хаяг руу грамм илгээж болно.

TON-д ухаалаг гэрээг нийтлэхийн тулд хүлээн авсан .boc файлыг хөнгөн клиент ашиглан блокчлон руу илгээх шаардлагатай болно (доорх талаар дэлгэрэнгүй). Гэхдээ нийтлэхээсээ өмнө та үүсгэсэн хаяг руу грамм шилжүүлэх хэрэгтэй, эс тэгвээс ухаалаг гэрээг нийтлэхгүй. Нийтлэгдсэний дараа та ухаалаг гэрээг гаднаас (жишээ нь, хөнгөн үйлчлүүлэгч ашиглах) эсвэл дотроос (жишээлбэл, нэг ухаалаг гэрээ TON дотор мессеж илгээх) мессеж илгээх замаар түүнтэй харилцах боломжтой.

Бид кодыг хэрхэн нийтэлж байгааг ойлгосноор энэ нь илүү хялбар болно. Бид юу бичихийг хүсч байгаагаа, хөтөлбөр маань хэрхэн ажиллахыг бараг мэддэг. Бичиж байхдаа бид үүнийг одоо байгаа ухаалаг гэрээнүүдэд хэрхэн хэрэгжүүлж байгааг хайж олох эсвэл хэрэгжүүлэх кодыг хардаг Fift и FunC албан ёсны репозитороос эсвэл албан ёсны бичиг баримтаас харна уу.

Тэмцээний бүх оролцогчид болон Telegram-ын ажилчид цуглардаг Telegram чат дотроос би олон удаа түлхүүр үг хайдаг байсан бөгөөд тэмцээний үеэр бүгд тэнд цугларч Fift, FunC-ийн талаар ярилцаж эхлэв. Өгүүллийн төгсгөлд байгаа холбоос.

Онолоос практикт шилжих цаг болжээ.

ТОН-той ажиллах орчинг бэлтгэх

Би MacOS дээрх нийтлэлд тайлбарласан бүх зүйлийг хийж, Docker дээрх цэвэр Ubuntu 18.04 LTS дээр давхар шалгасан.

Таны хийх ёстой хамгийн эхний зүйл бол татаж аваад суулгах явдал юм lite-client Үүний тусламжтайгаар та TON руу хүсэлт илгээх боломжтой.

Албан ёсны вэбсайт дээрх зааврууд нь суулгах процессыг нэлээд дэлгэрэнгүй бөгөөд тодорхой тайлбарлаж, зарим нарийн ширийн зүйлийг орхигдуулсан болно. Энд бид зааврын дагуу алга болсон хамаарлыг суулгаж байна. Би төсөл бүрийг өөрөө эмхэтгээгүй бөгөөд албан ёсны Ubuntu репозитороос суулгаагүй (Би ашигласан MacOS дээр brew).

apt -y install git 
apt -y install wget 
apt -y install cmake 
apt -y install g++ 
apt -y install zlib1g-dev 
apt -y install libssl-dev 

Бүх хамаарлыг суулгасны дараа та суулгаж болно lite-client, Fift, FunC.

Нэгдүгээрт, бид TON репозиторыг хамаарлынх нь хамт хувилдаг. Тохиромжтой болгохын тулд бид бүх зүйлийг хавтас дотор хийх болно ~/TON.

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

Хадгалах газар мөн хэрэгжүүлэлтүүдийг хадгалдаг Fift и FunC.

Одоо бид төслийг угсрахад бэлэн боллоо. Хадгалах кодыг хавтас болгон хуваасан ~/TON/ton. The ~/TON хавтас үүсгэх build үүн дотор төслийг цуглуул.

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

Ухаалаг гэрээ бичих гэж байгаа болохоор бид зөвхөн хэрэгтэй биш lite-clientГэхдээ Fift с FunC, тэгэхээр бүгдийг эмхэтгэе. Энэ бол хурдан процесс биш тул бид хүлээж байна.

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

Дараа нь ямар зангилааны талаарх мэдээллийг агуулсан тохиргооны файлыг татаж авна уу lite-client холбогдох болно.

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

TON-д анхны хүсэлтээ гаргаж байна

Одоо эхлүүлцгээе lite-client.

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

Хэрэв угсралт амжилттай болсон бол эхлүүлсний дараа та гэрлийн үйлчлүүлэгчийн зангилаатай холболтын бүртгэлийг харах болно.

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

Та тушаалыг ажиллуулж болно help мөн ямар командууд байгааг хараарай.

help

Энэ нийтлэлд ашиглах командуудыг жагсаацгаая.

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

Одоо бид гэрээг өөрөө бичихэд бэлэн боллоо.

Реализация

Санаа

Дээр бичсэнчлэн бидний бичиж байгаа ухаалаг гэрээ бол сугалаа.

Түүгээр ч барахгүй энэ нь тасалбар худалдаж аваад нэг цаг, өдөр, сар хүлээх шаардлагатай сугалаа биш, харин хэрэглэгч гэрээний хаяг руу шууд шилжүүлдэг сугалаа юм. N грамм, тэр даруй буцааж авдаг 2 * N грамм эсвэл алдах. Бид ялах магадлалыг ойролцоогоор 40% болгоно. Хэрэв төлбөр хийхэд хангалттай грамм байхгүй бол бид гүйлгээг цэнэглэх гэж үзнэ.

Түүнчлэн, бооцоог бодит цаг хугацаанд, тохиромжтой хэлбэрээр харах боломжтой байх нь чухал бөгөөд ингэснээр хэрэглэгч хожсон эсвэл хожигдсон эсэхийг шууд ойлгох болно. Тиймээс та ТОН-оос шууд бооцоо, үр дүнг харуулах вэб сайт хийх хэрэгтэй.

Ухаалаг гэрээ бичих

Тохиромжтой болгох үүднээс би FunC-ийн кодыг онцолсон; залгаасыг Visual Studio Code хайлтаас олж суулгаж болно; хэрэв та гэнэт ямар нэг зүйл нэмэхийг хүсвэл би залгаасыг олон нийтэд нээлттэй болгосон. Мөн хэн нэгэн өмнө нь Fift-тай ажиллах нэмэлт өргөтгөл хийсэн бол та үүнийг суулгаж, VSC дээрээс олох боломжтой.

Завсрын үр дүнг гаргах агуулахыг нэн даруй үүсгэцгээе.

Бидний амьдралыг хөнгөвчлөхийн тулд ухаалаг гэрээ бичээд бэлэн болтол нь орон нутагтаа турших болно. Үүний дараа л бид ТОН-оор нийтлэх болно.

Ухаалаг гэрээ нь нэвтрэх боломжтой хоёр гадаад аргатай. Эхлээд, recv_external() Энэ функц нь гадаад ертөнцөөс гэрээний хүсэлт ирэх үед, өөрөөр хэлбэл ТОН-оос биш, жишээлбэл, бид өөрсдөө мессеж үүсгэж, lite-client-ээр дамжуулан илгээх үед гүйцэтгэгддэг. Хоёрдугаарт, recv_internal() Энэ бол ТОН доторх аливаа гэрээ нь манайхыг хэлнэ. Аль ч тохиолдолд та функцэд параметрүүдийг дамжуулж болно.

Нийтлэгдсэн тохиолдолд ажиллах энгийн жишээнээс эхэлье, гэхдээ үүнд ямар ч функциональ ачаалал байхгүй.

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

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

Энд бид юу болохыг тайлбарлах хэрэгтэй slice. TON Blockchain-д хадгалагдсан бүх өгөгдөл нь цуглуулга юм TVM cell эсвэл зүгээр л cell, ийм нүдэнд та 1023 бит хүртэл өгөгдөл болон бусад нүднүүдийн 4 хүртэлх холбоосыг хадгалах боломжтой.

TVM cell slice буюу slice энэ нь одоо байгаа нэг хэсэг юм cell задлан шинжлэхэд ашигладаг бол дараа нь тодорхой болно. Бидний хувьд гол зүйл бол шилжүүлж чадна slice мөн мессежийн төрлөөс хамааран өгөгдлийг боловсруулах recv_external() буюу recv_internal().

impure — уг функц нь ухаалаг гэрээний өгөгдлийг өөрчилдөг гэсэн түлхүүр үг.

Гэрээний кодыг хадгалъя lottery-code.fc болон эмхэтгэх.

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

Тугуудын утгыг командыг ашиглан харж болно

~/TON/build/crypto/func -help

Бид Fift ассемблер кодыг эмхэтгэсэн 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

Үүнийг орон нутагтаа эхлүүлж болно, үүний тулд бид орчныг нь бэлтгэнэ.

Эхний мөр нь холбогдож байгааг анхаарна уу Asm.fif, энэ нь Fift ассемблерт зориулж Fift дээр бичигдсэн код юм.

Бид ухаалаг гэрээг дотооддоо ажиллуулж, туршихыг хүсч байгаа тул бид файл үүсгэх болно lottery-test-suite.fif мөн эмхэтгэсэн кодыг тэнд хуулж, хамгийн сүүлийн мөрийг сольж, ухаалаг гэрээний кодыг тогтмол болгож бичнэ. codeДараа нь виртуал машин руу шилжүүлэхийн тулд:

"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

Одоогоор энэ нь тодорхой юм шиг санагдаж байна, одоо бид 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 бид контекст, өөрөөр хэлбэл TVM (эсвэл сүлжээний төлөв) эхлэх өгөгдлийг бүртгэдэг. Тэмцээний үеэр ч хөгжүүлэгчдийн нэг нь хэрхэн бүтээхийг харуулсан c7 мөн би хуулсан. Энэ нийтлэлд бид өөрчлөх шаардлагатай байж магадгүй юм rand_seed Учир нь санамсаргүй тоо үүсэх нь үүнээс хамаардаг бөгөөд хэрэв өөрчлөгдөөгүй бол тэр болгонд ижил тоо буцаана.

recv_internal и recv_external 0 ба -1 утгатай тогтмолууд нь ухаалаг гэрээнд харгалзах функцуудыг дуудах үүрэгтэй.

Одоо бид хоосон ухаалаг гэрээний анхны тестийг бүтээхэд бэлэн байна. Тодорхой болгохын тулд одоохондоо бид бүх тестийг нэг файлд нэмэх болно lottery-test-suite.fif.

Хувьсагч үүсгэцгээе storage тэгээд дотор нь хоосон бичнэ үү cell, энэ нь ухаалаг гэрээний хадгалалт байх болно.

message Энэ бол бид гаднаас ухаалаг контакт руу дамжуулах мессеж юм. Бид бас одоохондоо хоосон болгоно.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Тогтмол болон хувьсагчдыг бэлтгэсний дараа бид командыг ашиглан TVM-г ажиллуулна runvmctx үүсгэсэн параметрүүдийг оролт руу дамжуулна.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Эцсийн эцэст бид амжилтанд хүрнэ үүн шиг -д зориулсан завсрын код Fift.

Одоо бид гарсан кодыг ажиллуулж болно.

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

Програм нь алдаагүй ажиллах ёстой бөгөөд гаралт дээр бид гүйцэтгэлийн бүртгэлийг харах болно.

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

Гайхалтай, бид ухаалаг гэрээний анхны хувилбарыг бичсэн.

Одоо бид функцийг нэмэх хэрэгтэй. Эхлээд гадаад ертөнцөөс ирж буй мессежүүдийг авч үзье recv_external()

Хөгжүүлэгч өөрөө гэрээгээр хүлээн зөвшөөрч болох мессежийн форматыг сонгодог.

Гэхдээ ихэвчлэн

  • Нэгдүгээрт, бид гэрээгээ гадаад ертөнцөөс хамгаалж, зөвхөн гэрээний эзэн түүнд гадны мессеж илгээх боломжтой болгохыг хүсч байна.
  • Хоёрдугаарт, бид TON руу хүчинтэй мессеж илгээх үед бид үүнийг яг нэг удаа хийхийг хүсч байгаа бөгөөд бид дахин ижил мессеж илгээх үед ухаалаг гэрээ үүнийг үгүйсгэдэг.

Тэгэхээр бараг бүх гэрээ энэ хоёр асуудлыг шийддэг, манай гэрээ гадны мессежийг хүлээн авдаг болохоор бид ч бас анхаарах хэрэгтэй.

Бид үүнийг урвуу дарааллаар хийх болно. Нэгдүгээрт, давтан асуудлаа шийдье, хэрэв гэрээ аль хэдийн ийм мессеж хүлээн авч, түүнийг боловсруулсан бол хоёр дахь удаагаа гүйцэтгэхгүй. Дараа нь бид зөвхөн тодорхой хүрээний хүмүүс ухаалаг гэрээ рүү мессеж илгээх боломжтой байхаар асуудлыг шийдэх болно.

Давхардсан мессежтэй холбоотой асуудлыг шийдэх янз бүрийн арга байдаг. Бид үүнийг хэрхэн хийх талаар эндээс үзнэ үү. Ухаалаг гэрээнд бид хүлээн авсан мессежийн тоолуурыг 0 анхны утгаар эхлүүлдэг. Ухаалаг гэрээний мессеж бүрт бид одоогийн тоолуурын утгыг нэмнэ. Хэрэв мессеж дэх тоолуурын утга нь ухаалаг гэрээний утгатай тохирохгүй бол бид үүнийг боловсруулахгүй, хэрэв таарвал бид үүнийг боловсруулж, ухаалаг гэрээний тоологчийг 1-ээр нэмэгдүүлнэ.

-руу буцаж орцгооё lottery-test-suite.fif үүн дээр хоёр дахь тестийг нэмнэ үү. Хэрэв бид буруу дугаар илгээвэл код нь онцгой тохиолдол гаргах ёстой. Жишээлбэл, гэрээний өгөгдөлд 166-г хадгалахыг зөвшөөрч, бид 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"

Эхлүүлье.

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

Мөн бид туршилтыг алдаатай гүйцэтгэж байгааг харах болно.

[ 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

Энэ үе шатанд lottery-test-suite.fif шиг харагдах ёстой холбоос.

Одоо ухаалаг гэрээнд эсрэг логикийг нэмье 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 Бидний илгээж буй мессеж нь худал юм.

Бидний хийх хамгийн эхний зүйл бол мессеж өгөгдөл агуулсан эсэхийг шалгах, хэрэв байхгүй бол бид зүгээр л гарна.

Дараа нь бид мессежийг задлан шинжилнэ. in_msg~load_uint(32) 165, 32 битийн тоог ачаална unsigned int дамжуулсан мессежээс.

Дараа нь бид ухаалаг гэрээний сангаас 32 бит ачаална. Ачаалагдсан тоо нь дамжуулсан дугаартай таарч байгаа эсэхийг шалгадаг, хэрэв үгүй ​​бол бид онцгой тохиолдол гаргадаг. Манай тохиолдолд нэг бус тоглолтыг давж байгаа тул үл хамаарах зүйл хийх ёстой.

Одоо эмхэтгэцгээе.

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

Гарсан кодыг дараах руу хуулна уу lottery-test-suite.fif, сүүлчийн мөрийг солихоо бүү мартаарай.

Туршилт амжилттай болсон эсэхийг бид шалгана:

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

Яг энд Та одоогийн үр дүнгийн дагуу холбогдох амлалтыг харж болно.

Ухаалаг гэрээний эмхэтгэсэн кодыг тестийн хамт файл руу байнга хуулах нь тохиромжгүй гэдгийг анхаарна уу, тиймээс бид кодыг тогтмол болгож бичих скрипт бичиж, эмхэтгэсэн кодыг тестүүддээ холбох болно. "include".

Төслийн хавтсанд файл үүсгэнэ үү build.sh дараах агуулгатай.

#!/bin/bash

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

Үүнийг гүйцэтгэх боломжтой болгоё.

chmod +x ./build.sh

Одоо гэрээг эмхэтгэхийн тулд манай скриптийг ажиллуулаарай. Гэхдээ үүнээс гадна бид үүнийг тогтмол болгон бичих хэрэгтэй code. Тиймээс бид шинэ файл үүсгэх болно lotter-compiled-for-test.fif, бид үүнийг файлд оруулах болно lottery-test-suite.fif.

sh-д skirpt код нэмье, энэ нь хөрвүүлсэн файлыг зүгээр л хуулбарлах болно lotter-compiled-for-test.fif ба түүний сүүлийн мөрийг өөрчил.

# 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

Одоо шалгахын тулд үүссэн скриптийг ажиллуулаад файл үүсгэгдэх болно lottery-compiled-for-test.fif, бид үүнийг манайд оруулах болно lottery-test-suite.fif

В lottery-test-suite.fif гэрээний кодыг устгаад мөрийг нэмнэ "lottery-compiled-for-test.fif" include.

Тэд тэнцсэн эсэхийг шалгахын тулд бид шалгалт явуулдаг.

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

Гайхалтай, одоо туршилтыг автоматжуулахын тулд файл үүсгэцгээе test.sh, аль нь эхлээд гүйцэтгэх болно build.sh, дараа нь туршилтуудыг ажиллуул.

touch test.sh
chmod +x test.sh

Бид дотроо бичдэг

./build.sh 

echo "nCompilation completedn"

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

Энийг хийцгээе test.sh туршилтууд ажиллаж байгаа эсэхийг шалгахын тулд үүнийг ажиллуул.

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

Бид гэрээг эмхэтгэж, туршилтыг хийж байгаа эсэхийг шалгадаг.

Гайхалтай, одоо эхэлж байна test.sh Шалгалтуудыг эмхэтгэн шууд ажиллуулна. Энд холбоос байна хийх.

За, үргэлжлүүлэхээсээ өмнө тав тухтай байлгах үүднээс өөр нэг зүйлийг хийцгээе.

Хавтас үүсгэцгээе build Хуулбарласан гэрээ болон түүний клоныг тогтмол болгож хадгална lottery-compiled.fif, lottery-compiled-for-test.fif. Мөн хавтас үүсгэцгээе test туршилтын файл хаана хадгалагдах вэ? lottery-test-suite.fif болон бусад туслах файлууд. Холбогдох өөрчлөлтүүдийн холбоос.

Ухаалаг гэрээг үргэлжлүүлэн хөгжүүлцгээе.

Дараа нь бид зөв дугаарыг илгээх үед мессеж хүлээн авсан, тоолуур дэлгүүрт шинэчлэгдсэн эсэхийг шалгах тест байх ёстой. Гэхдээ бид үүнийг дараа хийх болно.

Одоо ухаалаг гэрээнд ямар өгөгдлийн бүтэц, ямар өгөгдлийг хадгалах ёстой талаар бодож үзье.

Би бидний хадгалдаг бүх зүйлийг тайлбарлах болно.

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

Дараа нь та хоёр функц бичих хэрэгтэй. Эхнийхийг нь дуудъя pack_state(), дараа нь ухаалаг гэрээний санд хадгалах өгөгдлийг багцлах болно. Хоёрдахь руу залгая unpack_state() хадгалах сангаас өгөгдлийг уншиж, буцаана.

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

Бид ухаалаг гэрээний эхэнд эдгээр хоёр функцийг нэмдэг. Энэ нь бүтэх болно үүн шиг завсрын үр дүн.

Өгөгдлийг хадгалахын тулд та суулгасан функцийг дуудах хэрэгтэй болно set_data() ба энэ нь өгөгдлийг бичих болно pack_state() ухаалаг гэрээний хадгалах санд.

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

Одоо бидэнд өгөгдөл бичих, уншихад тохиромжтой функцууд байгаа тул бид цаашаа явж болно.

Бид гаднаас ирж буй мессежийг гэрээний эзэмшигч (эсвэл хувийн түлхүүрт хандах эрхтэй өөр хэрэглэгч) гарын үсэг зурсан эсэхийг шалгах хэрэгтэй.

Ухаалаг гэрээг нийтлэх үед бид үүнийг хадгалахад хэрэгтэй мэдээллээр эхлүүлэх боломжтой бөгөөд ирээдүйд ашиглахаар хадгалагдах болно. Ирж буй мессежийг холбогдох хувийн түлхүүрээр баталгаажуулахын тулд бид нийтийн түлхүүрийг тэнд бүртгэх болно.

Үргэлжлүүлэхийн өмнө хувийн түлхүүр үүсгээд түүнд бичье test/keys/owner.pk. Үүнийг хийхийн тулд Fift-ийг интерактив горимд ажиллуулж, дөрвөн командыг ажиллуулъя.

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

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

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

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

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

Хавтас үүсгэцгээе keys хавтас дотор test тэнд хувийн түлхүүрээ бичнэ үү.

mkdir test/keys
cd test/keys
~/TON/build/crypto/fift -i 
newkeypair
 ok
.s 
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128 
drop 
 ok
"owner.pk" B>file
 ok
bye

Бид одоогийн хавтсанд байгаа файлыг харж байна owner.pk.

Бид нийтийн түлхүүрийг стекээс устгаж, шаардлагатай бол хувийн түлхүүрээс авах боломжтой.

Одоо бид гарын үсгийн баталгаажуулалтыг бичих хэрэгтэй. Туршилтаас эхэлье. Эхлээд бид функцийг ашиглан файлаас хувийн түлхүүрийг уншина file>B мөн хувьсагч руу бичнэ owner_private_key, дараа нь функцийг ашиглана уу priv>pub хувийн түлхүүрийг нийтийн түлхүүр болгон хувиргаж, үр дүнг бичнэ үү 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 !

Бидэнд хоёр түлхүүр хэрэгтэй болно.

Бид ухаалаг гэрээний хадгалалтыг дурын өгөгдөл бүхий функцтэй ижил дарааллаар эхлүүлдэг pack_state()мөн хувьсагч болгон бичнэ 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 !

Дараа нь бид гарын үсэг зурсан мессеж бичих бөгөөд энэ нь зөвхөн гарын үсэг болон тоолуурын утгыг агуулна.

Эхлээд бид дамжуулахыг хүссэн өгөгдлөө үүсгэж, дараа нь хувийн түлхүүрээр гарын үсэг зурж, эцэст нь гарын үсэг зурсан мессежийг үүсгэдэг.

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 !  

Үүний үр дүнд бид ухаалаг гэрээ рүү илгээх мессеж хувьсагчаар бичигдсэн байдаг message_to_send, функцуудын тухай hashu, ed25519_sign_uint та уншиж чадна Fift баримт бичигт.

Тестийг ажиллуулахын тулд бид дахин дууддаг.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Энд байгаа Тесттэй файл энэ үе шатанд иймэрхүү харагдах ёстой.

Туршилтыг явуулъя, энэ нь амжилтгүй болох тул бид ухаалаг гэрээг өөрчлөх бөгөөд ингэснээр энэ форматын мессежийг хүлээн авч, гарын үсгийг баталгаажуулна.

Эхлээд бид мессежээс гарын үсгийн 512 битийг тоолж хувьсагчид бичнэ, дараа нь бид тоолуурын хувьсагчийн 32 битийг тоолно.

Бид ухаалаг гэрээний сангаас өгөгдлийг унших функцтэй тул үүнийг ашиглах болно.

Дараа нь хадгалах сантай шилжүүлсэн тоолуурыг шалгаж, гарын үсгийг шалгаж байна. Хэрэв ямар нэг зүйл таарахгүй бол бид тохирох кодоор онцгой тохиолдол гаргадаг.

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

Холбогдох амлалт энд байна.

Туршилтуудыг ажиллуулж, хоёр дахь шалгалт амжилтгүй болохыг харцгаая. Хоёр шалтгааны улмаас зурваст хангалттай бит байхгүй, санах ойд хангалттай бит байхгүй тул задлан шинжлэх үед код гацдаг. Бид илгээж буй мессеждээ гарын үсэг нэмж, хамгийн сүүлийн тестээс хадгалах санг хуулах хэрэгтэй.

Хоёр дахь туршилтанд бид мессежийн гарын үсэг нэмж, ухаалаг гэрээний хадгалалтыг өөрчлөх болно. Энд байгаа тест бүхий файл яг одоогийн байдлаар харагдаж байна.

Дөрөв дэх тестийг бичье, үүнд бид хэн нэгний хувийн түлхүүрээр гарын үсэг зурсан мессеж илгээх болно. Өөр хувийн түлхүүр үүсгээд файлд хадгалъя not-owner.pk. Бид энэ хувийн түлхүүрээр зурваст гарын үсэг зурах болно. Туршилтуудаа ажиллуулж, бүх шалгалтууд тэнцсэн эсэхийг шалгацгаая. Амлах энэ цаг мөчид.

Одоо бид эцэст нь ухаалаг гэрээний логикийг хэрэгжүүлэхэд шилжиж болно.
В recv_external() Бид хоёр төрлийн мессежийг хүлээн авах болно.

Бидний гэрээнд тоглогчдын алдагдлыг хуримтлуулах тул энэ мөнгийг сугалаа зохион бүтээгчид шилжүүлэх ёстой. Гэрээ байгуулах үед хонжворт сугалаа үүсгэгчийн түрийвчний хаягийг бүртгэнэ.

Ямар ч тохиолдолд бид ялагдсан хүмүүсийн грамм илгээдэг хаягийг өөрчлөх чадвартай байх хэрэгтэй. Бид мөн сугалааны сугалааны граммыг эзэмшигчийн хаяг руу илгээх боломжтой байх ёстой.

Эхнийхээс эхэлцгээе. Эхлээд мессеж илгээсний дараа ухаалаг гэрээ шинэ хаягийг хадгалах санд хадгалсан эсэхийг шалгах тест бичье. Мессежид тоолуур болон шинэ хаягаас гадна бид бас дамжуулж байгааг анхаарна уу action 7 битийн бүхэл сөрөг бус тоо, үүнээс хамааран бид ухаалаг гэрээнд мессежийг хэрхэн боловсруулахаа сонгох болно.

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

Туршилтаас та ухаалаг гэрээний хадгалалт хэрхэн цуваагүй болохыг харж болно storage Тав дахь. Хувьсагчийн цуваа салалтыг Fift баримт бичигт тайлбарласан болно.

Холбоосыг оруулах нэмсэн зуурсан гурилтай.

Туршилтыг хийж, амжилтгүй болсон эсэхийг шалгацгаая. Одоо сугалааны эзэмшигчийн хаягийг өөрчлөх логикийг оруулъя.

Ухаалаг гэрээнд бид үргэлжлүүлэн задлан шинжилдэг message, уншина уу action. Бид хоёр байх болно гэдгийг сануулъя action: хаягийг сольж, грамм илгээнэ үү.

Дараа нь бид гэрээ эзэмшигчийн шинэ хаягийг уншиж, хадгалахад хадгална.
Бид туршилтыг явуулж, гурав дахь шалгалт амжилтгүй болохыг харлаа. Гэрээ нь туршилтанд дутуу байгаа мессежийн 7 битийг нэмж задлан шинжилж байгаа тул энэ нь сүйрчээ. Зурвас дээр байхгүй нэгийг нэмнэ үү action. Туршилтуудыг хийж, бүх зүйл амжилттай болж байгааг харцгаая. энд өөрчлөлт хийхийг амлаж байна. Агуу их.

Одоо өмнө нь хадгалсан хаяг руу заасан тооны граммыг илгээх логикийг бичье.

Эхлээд тест бичье. Бид хоёр тест бичих болно, нэг нь хангалттай тэнцэл байхгүй үед, хоёр дахь нь бүх зүйл амжилттай давах ёстой. Туршилтуудыг үзэх боломжтой энэ амлалтанд.

Одоо кодыг нэмье. Эхлээд хоёр туслах аргыг бичье. Эхний авах арга бол ухаалаг гэрээний одоогийн үлдэгдлийг олох явдал юм.

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

Хоёр дахь нь өөр ухаалаг гэрээнд грамм илгээх зориулалттай. Би энэ аргыг өөр ухаалаг гэрээнээс бүрэн хуулбарласан.

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

Ухаалаг гэрээнд энэ хоёр аргыг нэмээд логикийг нь бичье. Эхлээд бид мессежээс хэдэн граммыг задлан шинжилдэг. Дараа нь бид үлдэгдлийг шалгана, хэрэв хангалттай биш бол бид үл хамаарах зүйл хийнэ. Хэрэв бүх зүйл зүгээр бол бид граммаа хадгалсан хаяг руу илгээж, тоолуурыг шинэчилнэ.

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

Энд байгаа одоогоор ухаалаг гэрээ шиг харагдаж байна. Шалгалтуудыг ажиллуулж, тэнцсэн эсэхийг шалгацгаая.

Дашрамд хэлэхэд, боловсруулсан мессеж бүрт ухаалаг гэрээнээс шимтгэл суутгадаг. Ухаалаг гэрээний мессеж хүсэлтийг биелүүлэхийн тулд үндсэн шалгалтын дараа та залгах хэрэгтэй accept_message().

Одоо дотоод мессежүүд рүү шилжье. Үнэн хэрэгтээ, бид зөвхөн граммыг хүлээн авч, тоглогч хожвол хоёр дахин, хожигдсон тохиолдолд гуравны нэгийг эзэмшигчид нь буцааж илгээх болно.

Эхлээд энгийн тест бичье. Үүнийг хийхийн тулд бидэнд ухаалаг гэрээ рүү грамм илгээдэг ухаалаг гэрээний туршилтын хаяг хэрэгтэй.

Ухаалаг гэрээний хаяг нь ажлын хэлхээг хариуцах 32 битийн бүхэл тоо болон энэ ажлын хэлхээнд 256 бит сөрөг бус бүхэл тоон өвөрмөц дансны дугаар гэсэн хоёр тооноос бүрдэнэ. Жишээлбэл, -1 ба 12345, энэ нь бидний файлд хадгалах хаяг юм.

Би хаягийг хадгалах функцийг хуулсан 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

Функц хэрхэн ажилладагийг харцгаая, энэ нь Fift хэрхэн ажилладаг талаар ойлголт өгөх болно. Fift-ийг интерактив горимд ажиллуулна уу.

~/TON/build/crypto/fift -i 

Эхлээд бид -1, 12345 болон ирээдүйн файлын "sender.addr" нэрийг стек дээр дарна.

-1 12345 "sender.addr" 

Дараагийн алхам бол функцийг гүйцэтгэх явдал юм -rot, энэ нь стекийн дээд хэсэгт өвөрмөц ухаалаг гэрээний дугаар байхаар стекийг шилжүүлдэг:

"sender.addr" -1 12345

256 u>B 256 битийн сөрөг бус бүхэл тоог байт руу хөрвүүлдэг.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap стекийн дээд хоёр элементийг солино.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B 32 битийн бүхэл тоог байт руу хөрвүүлдэг.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ хоёр байт дарааллыг холбодог.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Дахиад л swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Эцэст нь байтуудыг файлд бичнэ B>file. Үүний дараа бидний стек хоосон байна. Бид зогсоо Fift. Одоогийн фолдерт файл үүсгэгдсэн байна sender.addr. Файлыг үүсгэсэн хавтас руу шилжүүлье test/addresses/.

Ухаалаг гэрээнд грамм илгээх энгийн тест бичье. Энд амлалт байна.

Одоо сугалааны логикийг харцгаая.

Бидний хийх хамгийн эхний зүйл бол мессежийг шалгах явдал юм bounced эсвэл үгүй ​​бол bounced, дараа нь бид үүнийг үл тоомсорлодог. bounced ямар нэг алдаа гарвал гэрээ грамм буцаана гэсэн үг. Гэнэт алдаа гарвал бид граммыг буцааж өгөхгүй.

Бид шалгаж, хэрэв үлдэгдэл хагас граммаас бага байвал мессежийг хүлээн авч, үл тоомсорлодог.

Дараа нь бид мессеж ирсэн ухаалаг гэрээний хаягийг задлан шинжилнэ.

Бид хадгалах сангаас өгөгдлийг уншиж, хэрэв хорь гаруй бол хуучин бооцоог түүхээс устгана. Тохиромжтой болгохын тулд би гурван нэмэлт функц бичсэн pack_order(), unpack_order(), remove_old_orders().

Дараа нь бид үлдэгдэл төлбөр төлөхөд хүрэлцэхгүй байгаа бол энэ нь бооцоо биш, харин нөхөн төлбөр гэж үзэж, нөхөн төлбөрөө хадгална. orders.

Дараа нь эцэст нь ухаалаг гэрээний мөн чанар.

Нэгдүгээрт, хэрэв тоглогч хожигдсон бол бид бооцооны түүхэнд хадгалдаг бөгөөд хэрэв хэмжээ нь 3 граммаас их бол бид ухаалаг гэрээний эзэн рүү 1/3-ийг илгээдэг.

Хэрэв тоглогч хожсон бол бид хоёр дахин их дүнг тоглогчийн хаяг руу илгээж, бооцооны талаарх мэдээллийг түүхэнд хадгална.

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

Энэ бүхэн. Харгалзах үүрэг.

Одоо үлдсэн бүх зүйл нь энгийн бөгөөд бид гэрээний төлөв байдлын талаар гадаад ертөнцөөс мэдээлэл авахын тулд авах аргуудыг бий болгоцгооё (үнэндээ тэдний ухаалаг гэрээний сангаас өгөгдлийг уншина уу).

Авах аргуудыг нэмье. Ухаалаг гэрээний талаарх мэдээллийг хэрхэн хүлээн авах талаар бид доор бичих болно.

Ухаалаг гэрээг нийтлэх үед гарах хамгийн анхны хүсэлтийг боловсруулах кодыг нэмэхээ мартсан байна. Харгалзах үүрэг. Тэгээд цааш нь зассан үнийн дүнгийн 1/3-ийг эзэмшигчийн данс руу илгээсэн алдаа.

Дараагийн алхам бол ухаалаг гэрээг нийтлэх явдал юм. Хавтас үүсгэцгээе requests.

Би хэвлэлийн кодыг үндэс болгон авсан simple-wallet-code.fc тэр нь олж чадна албан ёсны санд.

Анхаарал хандуулах ёстой зүйл. Бид ухаалаг гэрээний хадгалалт болон оролтын мессежийг үүсгэдэг. Үүний дараа ухаалаг гэрээний хаяг үүсдэг, өөрөөр хэлбэл TON-д хэвлэгдэхээс өмнө хаяг нь мэдэгддэг. Дараа нь та энэ хаяг руу хэд хэдэн грамм илгээх шаардлагатай бөгөөд үүний дараа ухаалаг гэрээ, түүн доторх үйлдлүүдийг хадгалах комисс (ухаалаг мэдээллийг хадгалдаг, гүйцэтгэдэг баталгаажуулагч) авдаг тул та ухаалаг гэрээтэй файлыг өөрөө илгээх хэрэгтэй. гэрээ). Кодыг эндээс харж болно.

Дараа нь бид нийтлэх кодыг ажиллуулж, авна lottery-query.boc ухаалаг гэрээний файл болон хаяг.

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

Үүсгэсэн файлуудыг хадгалахаа бүү мартаарай: lottery-query.boc, lottery.addr, lottery.pk.

Бусад зүйлсийн дотор бид ухаалаг гэрээний хаягийг гүйцэтгэлийн бүртгэлээс харах болно.

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

Зүгээр л хөгжилдөхийн тулд ТОН-д хүсэлт гаргая

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

Мөн бид энэ хаягтай данс хоосон байгааг харах болно.

account state is empty

Бид хаягаар илгээдэг 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 грамм, хэдхэн секундын дараа бид ижил тушаалыг гүйцэтгэдэг. Грам илгээхийн тулд би ашигладаг албан ёсны түрийвч, мөн та чатаас хэн нэгнээс тестийн грамм асууж болно, би энэ талаар өгүүллийн төгсгөлд ярих болно.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Эхлээгүй юм шиг харагдаж байна (state:account_uninit) ижил хаягтай, 1 нанограмм тэнцэх ухаалаг гэрээ.

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

Одоо ухаалаг гэрээг нийтэлье. Lite-client-ийг ажиллуулж, ажиллуулцгаая.

> 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 

Гэрээ хэвлэгдсэн эсэхийг шалгая.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Бусад зүйлсийн дунд бид авдаг.

  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

Бид үүнийг харж байна account_active.

Өөрчлөлтийг дагаж мөрдөх энд байна.

Одоо ухаалаг гэрээтэй харилцах хүсэлтийг үүсгэцгээе.

Бүр тодруулбал, бид эхнийх нь хаягийг солих ажлыг бие даасан ажил болгон үлдээж, хоёр дахь нь эзэмшигчийн хаяг руу грамм илгээх болно. Үнэн хэрэгтээ бид грамм илгээх тесттэй ижил зүйлийг хийх шаардлагатай болно.

Энэ бол ухаалаг гэрээнд бид илгээх мессеж юм msg_seqno 165, action Илгээхэд 2 ба 9.5 грамм.

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

Өөрийн хувийн түлхүүрээр зурваст гарын үсэг зурахаа бүү мартаарай lottery.pkУхаалаг гэрээг бий болгоход өмнө нь үүсгэсэн . Харгалзах үүрэг энд байна.

Get аргуудыг ашиглан ухаалаг гэрээнээс мэдээлэл хүлээн авах

Одоо ухаалаг гэрээ авах аргуудыг хэрхэн ажиллуулах талаар харцгаая.

Эхлэх lite-client мөн бидний бичсэн get аргуудыг ажиллуул.

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

В result функцийн буцаадаг утгыг агуулна balance() бидний ухаалаг гэрээнээс.
Бид өөр хэд хэдэн аргын хувьд ижил зүйлийг хийх болно.

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

Таны бооцооны түүхийг асууя.

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

Бид lite-client-ийг ашиглаж, ухаалаг гэрээний талаарх мэдээллийг сайт дээр харуулах аргуудыг авах болно.

Ухаалаг гэрээний өгөгдлийг вэбсайт дээр харуулах

Би ухаалаг гэрээний өгөгдлийг хялбар байдлаар харуулахын тулд Python дээр энгийн вэбсайт бичсэн. Энд би энэ талаар дэлгэрэнгүй ярихгүй бөгөөд сайтыг нийтлэх болно нэг амлалтанд.

ТОН-ын хүсэлтийг Python тусламжтайгаар lite-client. Тохиромжтой болгох үүднээс сайтыг Docker-д багцалж, Google Cloud дээр нийтэлсэн. Холбоос.

Хичээж байна

Одоо тэнд граммаа нөхөхөөр илгээхийг оролдъё түрийвч. Бид 40 грамм илгээх болно. Тодорхой болгохын тулд хэд хэдэн бооцоо тавьцгаая. Энэ сайт нь бооцооны түүх, одоогийн хожлын хувь болон бусад хэрэгтэй мэдээллийг харуулдаг болохыг бид харж байна.

Бид харж байнаБид эхний ялсан, хоёр дахь нь ялагдсан.

Дараах үгс

Нийтлэл миний бодож байснаас хамаагүй урт болсон, магадгүй богинохон ч байж магадгүй, эсвэл зүгээр л ТОН-ын талаар юу ч мэдэхгүй, түүнтэй харилцах чадвартай, тийм ч энгийн биш ухаалаг гэрээ бичиж, нийтлэхийг хүсдэг хүмүүст зориулагдсан байж магадгүй юм. тэр. Магадгүй зарим зүйлийг илүү энгийнээр тайлбарлаж болох байсан.

Магадгүй хэрэгжилтийн зарим талыг илүү үр дүнтэй, гоёмсог байдлаар хийж болох байсан ч нийтлэлийг бэлтгэхэд илүү их цаг хугацаа шаардагдах байсан. Би хаа нэгтээ алдаа гаргасан эсвэл ямар нэг зүйлийг ойлгоогүй байж магадгүй тул хэрэв та ноцтой зүйл хийж байгаа бол албан ёсны баримт бичиг эсвэл TON код бүхий албан ёсны репозиторыг найдах хэрэгтэй.

TON өөрөө хөгжлийн идэвхтэй үе шатандаа байгаа тул энэ нийтлэлийн аль нэг алхмыг эвдэх өөрчлөлтүүд гарч болзошгүй гэдгийг тэмдэглэх нь зүйтэй (би бичиж байх үед энэ нь аль хэдийн засагдсан) боловч ерөнхий арга барил нь өөрчлөгдөх магадлал багатай.

Би ТОН-ын ирээдүйн талаар ярихгүй. Магадгүй платформ нь том зүйл болж, бид үүнийг судлахад цаг зарцуулж, одоо бүтээгдэхүүнээрээ орон зайг дүүргэх хэрэгтэй.

ТОН-оос дээш хэрэглэгчдийн боломжит үзэгчидтэй Facebook-ийн Libra бас бий. Би Либрагийн талаар бараг юу ч мэдэхгүй, форумаас харахад ТОН нийгэмлэгээс хамаагүй илүү үйл ажиллагаа байдаг. Хэдийгээр TON-ийн хөгжүүлэгчид болон нийгэмлэгүүд газар доорхи газартай төстэй байдаг нь бас гайхалтай юм.

лавлагаа

  1. Албан ёсны TON баримт бичиг: https://test.ton.org
  2. Албан ёсны TON хадгалах газар: https://github.com/ton-blockchain/ton
  3. Төрөл бүрийн платформд зориулсан албан ёсны түрийвч: https://wallet.ton.org
  4. Энэ нийтлэлийн ухаалаг гэрээний агуулах: https://github.com/raiym/astonished
  5. Ухаалаг гэрээний вэбсайтын холбоос: https://ton-lottery.appspot.com
  6. FunC-д зориулсан Visual Studio кодын өргөтгөлийн агуулах: https://github.com/raiym/func-visual-studio-plugin
  7. Telegram дээр TON-ийн талаар чатлах нь эхний шатанд үүнийг ойлгоход үнэхээр тусалсан. TON-д зориулж ямар нэгэн зүйл бичсэн хүн бүр тэнд байдаг гэвэл алдаа болохгүй гэж бодож байна. Та мөн тэндээс тестийн грамм асууж болно. https://t.me/tondev_ru
  8. Би хэрэгтэй мэдээлэл олж авсан TON-ийн талаархи өөр нэг чат: https://t.me/TONgramDev
  9. Тэмцээний эхний шат: https://contest.com/blockchain
  10. Тэмцээний хоёрдугаар шат: https://contest.com/blockchain-2

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх