ProHoster > блог > адміністраванне > Тэставы кліент TON (Telegram Open Network) і новая мова Fift для смарт-кантрактаў
Тэставы кліент TON (Telegram Open Network) і новая мова Fift для смарт-кантрактаў
Больш за год таму стала вядома аб планах месэнджэра Telegram выпусціць уласную дэцэнтралізаваную сетку Адкрытая сеткавая тэлеграма. Тады стаў даступны аб'ёмны тэхнічны дакумент, які, як мяркуецца, быў напісаны Мікалаем Дуравым і апісваў структуру будучай сеткі. Для тых, хто прапусціў - рэкамендую азнаёміцца з маім пераказам гэтага дакумента (частка 1, частка 2; трэцяя частка, нажаль, усё яшчэ пыліцца ў чарнавіках).
З таго часу ніякіх значных навін аб статусе распрацоўкі TON не было, пакуль пару дзён таму (у адным з неафіцыйных каналаў) не з'явілася спасылка на старонку https://test.ton.org/download.html, дзе размешчаны:
◦ ton-test-liteclient-full.tar.xz - Зыходнікі лёгкага кліента для тэставай сеткі TON;
◦ ton-lite-client-test1.config.json - канфігурацыйны файл для падлучэння да тэставай сеткі;
◦ README - інфармацыя аб зборцы і запуску кліента;
◦ ЯК - пакрокавая інструкцыя аб стварэнні смарт-кантракту з дапамогай кліента;
◦ ton.pdf — абноўлены дакумент (ад 2 сакавіка 2019 г.) з тэхнічным аглядам сеткі TON;
◦ tvm.pdf - тэхнічнае апісанне TVM (TON Virtual Machine, віртуальнай машыны TON);
◦ tblkch.pdf - Тэхнічнае апісанне блокчейна TON;
◦ fiftbase.pdf - апісанне новай мовы Fift, прызначанага для стварэння смарт-кантрактаў у TON.
Паўтаруся, афіцыйных пацверджанняў старонкі і ўсіх гэтых дакументаў з боку Тэлеграма не было, але аб'ём гэтых матэрыялаў робіць іх дастаткова праўдападобнымі. Запуск апублікаванага кліента здзяйсняйце на свой страх і рызыку.
Зборка тэставага кліента
Для пачатку паспрабуем сабраць і запусціць тэставы кліент - балазе, README падрабязна апісвае гэты нескладаны працэс. Я буду гэта рабіць на прыкладзе macOS 10.14.5, за паспяховасць зборкі на іншых сістэмах ручацца не магу.
Спампоўваем і распакоўваем архіў з зыходнікамі. Важна спампоўваць апошнюю версію, бо зваротная сумяшчальнасць на дадзеным этапе не гарантуецца.
Пераканаемся, што ў сістэме ўсталяваныя апошнія версіі make, cmake (версіі 3.0.2 або вышэй), OpenSSL (уключаючы загалоўкавыя файлы C), g++ ці clang. Мне нічога даўсталёўваць не прыйшлося, усё сабралася адразу.
Выкажам здагадку, зыходнікі распакаваны ў тэчку ~/lite-client. Асобна ад яе ствараем пустую тэчку для сабранага праекту (напрыклад, ~/liteclient-build), і з яе (cd ~/liteclient-build) выклікаем каманды:
Калі ўсё зроблена правільна, то вы павінны ўбачыць нешта такое:
Даступных каманд, як бачым, няшмат:
◦ help - вывесці гэты спіс каманд;
◦ quit - выйсці;
◦ time - паказаць бягучы час на серверы;
◦ status - паказаць стан падключэння і лакальнай БД;
◦ last - абнавіць стан блокчейна (загрузіць апошні блок). Гэтую каманду важна выконваць перад любымі запытамі, каб быць упэўненым, што Вы бачыце менавіта актуальны стан сеткі.
◦ sendfile<filename> - загрузіць лакальны файл у сетку TON. Так адбываецца ўзаемадзеянне з сеткай - у тым ліку, напрыклад, стварэнне новых смарт-кантрактаў і запыты на перавод сродкаў паміж акаўнтамі;
◦ getaccount<address> - паказаць бягучае (на момант выканання каманды last) стан акаўнта з указаным адрасам;
◦ privkey<filename> - загрузіць прыватны ключ з лакальнага файла.
Калі пры запуску кліента перадаць яму тэчку з дапамогай опцыі -D, то ён будзе складаць у яе апошні блок майстэрні:
Цяпер можам перайсці да цікавейшых рэчаў - вывучыць мову Fift, паспрабаваць скампіляваць смарт-кантракт (напрыклад, стварыць тэставы кашалёк), загрузіць яго ў сетку і паспрабаваць пераклад сродкаў паміж акаўнтамі.
Мова Fift
З дакумента fiftbase.pdf можна даведацца, што для стварэння смарт-кантрактаў каманда Telegram стварыла новую сцяковую мову Fift (мабыць, ад лічэбніка пяты, па аналогіі з Forth - мовай, з якім у Fift шмат агульнага).
Дакумент дастаткова аб'ёмны, на 87 старонак, і я не стану падрабязна пераказваць яго змест у рамках гэтага артыкула (як мінімум, таму што сам не скончыў яго чытанне :). Спынюся на асноўных момантах і прывяду пару прыкладаў кода на гэтай мове.
На базавым узроўні, сінтаксіс Фіфта дастаткова просты: яго код складаецца з слоў, як правіла, падзеленых прабеламі або перакладамі радкоў (прыватны выпадак: некаторыя словы не патрабуюць падзельніка пасля сябе). Любое слова — гэта рэгістра-залежная паслядоўнасць сімвалаў, якой адпавядае некаторае вызначэнне (грубіянска кажучы, тое, што інтэрпрэтатар павінен зрабіць, калі сустракае гэтае слова). Калі азначэння слова няма, інтэрпрэтатар спрабуе распарсіць яго як лік і пакласці на стэк. Дарэчы, лікі тут - раптам - 257-бітныя цэлыя, а дробавых няма зусім - дакладней, яны адразу ператвараюцца ў пару цэлых, якія ўтвараюць лічнік і назоўнік рацыянальнага дробу.
Словы, як правіла, узаемадзейнічаюць са значэннямі, якія ляжаць на верхавіне стэка. Асобны тып слоў прэфіксны - выкарыстоўвае не стэк, а наступныя за імі знакі з зыходнага файла. Напрыклад, так рэалізаваны радковыя літаралы - сімвал «двукоссе» (") з'яўляецца прэфіксным словам, якое шукае наступную (якая зачыняе) двукоссе, і змяшчае радок паміж імі на стэк. Падобнай жа выявай паводзяць сябе аднарадковыя (//) і шматрадковыя (/*) каментары.
На гэтым амаль уся ўнутраная прылада мовы сканчаецца. Усё астатняе (уключаючы кіраўнікі канструкцыі) вызначана як словы (альбо ўнутраныя, такія як арыфметычныя аперацыі і вызначэнне новых слоў; альбо вызначаныя ў "стандартнай бібліятэцы") Fift.fif, якая ляжыць у тэчцы crypto/fift у зыходніках).
Просты прыклад праграмы на Fift:
{ dup =: x dup * =: y } : setxy
3 setxy x . y . x y + .
7 setxy x . y . x y + .
У першым радку вызначаецца новае слова setxy (звярніце ўвагу на прэфікс {, які стварае блок да закрывае } і прэфікс :, які ўласна вызначае слова). setxy бярэ лік з вяршыні стэка, вызначае (або перавызначае) яго як глабальную канстантуx, а квадрат гэтага ліку - як канстанту y (улічваючы, што значэнні канстант можна перавызначаць, я б хутчэй назваў іх зменнымі, але я прытрымліваюся наймення ў мове).
У наступных двух радках на стэк кладзецца лік, выклікаецца setxy, затым выводзяцца значэння канстант x, y (для вываду выкарыстоўваецца слова .), абедзве канстанты змяшчаюцца на стэк, сумуюцца і вынік таксама выводзіцца. У выніку мы ўбачым:
3 9 12 ok
7 49 56 ok
(Страчку "ok" выводзіць інтэрпрэтатар, калі заканчвае апрацоўваць бягучы радок у інтэрактыўным рэжыме ўводу)
Гэты страшнавата выглядае файл прызначаны для стварэння смарт-кантракту - ён будзе змешчаны ў файл new-wallet-query.boc пасля выканання. Звярніце ўвагу, што тут выкарыстоўваецца яшчэ адна, асэмблерная мова для TON Virtual Machine (на ёй я не буду спыняцца падрабязна), інструкцыі якога і будуць змешчаны ў блокчейн.
Такім чынам, асэмблер для TVM напісаны на Fift - зыходнікі гэтага асэмблера знаходзяцца ў файле crypto/fift/Asm.fif і падлучаюцца ў пачатку прыведзенага вышэй кавалка кода.
Што я магу сказаць, відаць, Мікалай Дураў проста любіць ствараць новыя мовы праграмавання 🙂
Стварэнне смарт-кантракту і ўзаемадзеянне з TON
Такім чынам, напрыклад, мы сабралі кліент TON і інтэрпрэтатар Fift, як апісана вышэй, і пазнаёміліся з мовай. Як зараз стварыць смарт-кантракт? Пра гэта распавядаецца ў файліку ЯК, прыкладзеным да зыходнікаў.
Акаўнты ў TON
Як я апісваў у аглядзе TON, гэтая сетка змяшчае больш за адзін блокчейна - ёсць адзін агульны, т.зв. "майстэрчэйн", а таксама адвольная колькасць дадатковых "воркчэйнаў", якія ідэнтыфікуюцца 32-бітным лікам. Мастэрчэйн мае ідэнтыфікатар -1, акрамя яго гэтак жа можа выкарыстоўвацца «базавы» воркчэйн з ідэнтыфікатарам 0. У кожнага воркчэйна можа быць свая канфігурацыя. Унутрана кожны воркчэйн дробніцца на шардчэйны, але гэта ўжо дэталь рэалізацыі, якую неабавязкова памятаць.
У межах аднаго воркчэйна захоўваецца мноства акаўнтаў, у якіх ёсць свае ідэнтыфікатары account_id. Для мастерчейна і нулявога воркчэйна яны маюць даўжыню 256 біт. Такім чынам, ідэнтыфікатар акаўнта запісваецца, напрыклад, так:
Гэта "сыры" фармат: спачатку ідэнтыфікатар воркчэйна, затым двокроп'е, і ідэнтыфікатар акаўнта ў шаснаццатковым запісе.
Акрамя таго, ёсць скарочаны фармат - нумар воркчэйна і адрас акаўнта кадуюцца ў бінарным выглядзе, да іх дапісваецца кантрольная сума і ўсё гэта кадуецца ў Base64:
Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb
Ведаючы гэты фармат запісу, мы можам запытаць бягучы стан якога-небудзь акаўнта праз тэставы кліент з дапамогай каманды
[ 3][t 2][1558746708.815218925][test-lite-client.cpp:631][!testnode] requesting account state for -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D
[ 3][t 2][1558746708.858564138][test-lite-client.cpp:652][!testnode] got account state for -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D with respect to blocks (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F and (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:x8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:539)
public_cells:(var_uint len:0 value:0)) last_paid:0
due_payment:nothing)
storage:(account_storage last_trans_lt:74208000003
balance:(currencies
grams:(nanograms
amount:(var_uint len:7 value:999928362430000))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{0000000D}
))
library:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000000000000000451C90E00DC0E35B7DB5FB8C134_}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}
Бачым структуру, якая захоўваецца ў DHT названага воркчэйна. Напрыклад, у полі storage.balance знаходзіцца бягучы баланс акаўнта, у storage.state.code - код смарт-кантракту, а ў storage.state.data - Яго бягучыя дадзеныя. Звярніце ўвагу, што сховішча дадзеных TON – Cell, вочкі – з'яўляецца дрэвападобным, у кожнага вочка могуць быць як свае дадзеныя, так і даччыныя вочкі. Гэта паказана ў выглядзе водступаў у апошніх радках.
Зборка смарт-кантракта
Цяпер давайце створым самі такую структуру (яна называецца BOC - bag of cells) з дапамогай мовы Fift. На шчасце, самастойна пісаць смарт-кантракт не давядзецца - у тэчцы crypto/block з архіва з зыходнікамі ёсць файл new-wallet.fif, які дапаможа стварыць нам новы кашалёк. Скапіюем яго ў тэчку з сабраным кліентам (~/liteclient-build, калі вы дзейнічалі па інструкцыі вышэй). Яго ж змесціва я прыводзіў вышэй у якасці прыкладу кода на Fift.
Тут <source-directory> трэба замяніць на шлях да распакаваных зыходнікаў (знак «~» тут, нажаль, выкарыстаць нельга, патрэбен поўны шлях). Замест выкарыстання ключа -I можна вызначыць зменную асяроддзі FIFTPATH і змясціць гэты шлях у яе.
Бо Fift мы запусцілі з імем файла new-wallet.fif, ён выканае яго і завершыцца. Калі імя файла апусціць, то можна пагуляць з інтэрпрэтатарам у інтэрактыўным рэжыме.
У кансоль пасля выканання павінна вывесціся нешта такое:
StateInit: x{34_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
new wallet address = -1 : 4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ
signing message: x{00000000}
External message for initialization is x{89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
B5EE9C724104030100000000D60002CF89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001001020084FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED5400480000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B6290698B
(Saved to file new-wallet-query.boc)
Гэта азначае, што кашалёк з ідэнтыфікатарам -1:4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2 (або, што тое ж самае, 0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ) паспяхова створаны. Які адпавядае яму код апынецца ў файле new-wallet-query.boc, яго адрас - у new-wallet.addr, а прыватны ключ - у new-wallet.pk (будзьце асцярожныя - паўторны запуск скрыпту перазапіша гэтыя файлы).
Вядома, сетка TON пра гэты кашалёк яшчэ не ведае, ён захоўваецца толькі ў выглядзе гэтых файлаў. Цяпер яго трэба загрузіць у сетку. Праўда, праблема ў тым, што для стварэння смарт-кантракта трэба заплаціць камісію, а баланс у вашага акаўнта пакуль нулявы.
У працоўным рэжыме гэтая праблема вырашыцца пакупкай грамаў на біржы (або пераводам з іншага кашалька). Ну а ў цяперашнім тэставым рэжыме заведзены спецыяльны смарт-кантракт, у якога можна папрасіць да 20 грамаў проста так.
Фарміраванне запыту да чужога смарт-кантракта
Запыт да смарт-кантракту, які раздае грамы налева і направа, які робіцца так. Ва ўсё той жа тэчцы crypto/block знаходзім файл testgiver.fif:
// "testgiver.addr" file>B 256 B>u@
0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
dup constant wallet_addr ."Test giver address = " x. cr
0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
constant dest_addr
-1 constant wc
0x00000011 constant seqno
1000000000 constant Gram
{ Gram swap */ } : Gram*/
6.666 Gram*/ constant amount
// b x --> b' ( serializes a Gram amount )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 4 u, -rot 8 * u, } : Gram,
// create a message (NB: 01b00.., b = bounce)
<b b{010000100} s, wc 8 i, dest_addr 256 u, amount Gram, 0 9 64 32 + + 1+ 1+ u, "GIFT" $, b>
<b seqno 32 u, 1 8 u, swap ref, b>
dup ."enveloping message: " <s csr. cr
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 Gram, b{00} s,
swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
"wallet-query.boc" B>file
Яго таксама захаваем у тэчку з сабраным кліентам, але паправім пяты радок - перад радком.constant dest_addr“. Заменім яе на адрас таго кашалька, які вы стварылі да гэтага (поўны, не скарочаны). "-1:" у пачатку пісаць не трэба, замест гэтага ў пачатку пастаўце "0x".
Яшчэ можна памяняць радок 6.666 Gram*/ constant amount - Гэта сума ў грамах, якую вы запытваеце (не больш за 20). Нават калі паказваеце цэлы лік, пакіньце дзесятковую кропку.
Нарэшце, трэба паправіць радок 0x00000011 constant seqno. Першы лік тут - гэта бягучы sequence number, які захоўваецца ў акаўнце, які выдае грамы. Адкуль яго ўзяць? Як гаварылася вышэй, запусціце кліент і выканайце:
last
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
Лік 0000000D (у вас яно будзе больш) і есць sequence number, які трэба падставіць у testgiver.fif.
Усё, захоўваем файл і запускаем (./crypto/fift testgiver.fif). На выхадзе атрымаем файл wallet-query.boc. Гэта і ёсць сфарміраванае паведамленне да чужога смарт-кантракту - просьба "перавядзі столькі грамаў на вось такі акаўнт".
З дапамогай кліента загружаем яго ў сетку:
> sendfile wallet-query.boc
[ 1][t 1][1558747399.456575155][test-lite-client.cpp:577][!testnode] sending query from file wallet-query.boc
[ 3][t 2][1558747399.500236034][test-lite-client.cpp:587][!query] external message status is 1
Калі зараз выклікаць last, а затым зноў запытаць статус акаўнта, у якога мы папрасілі грамы, то мы павінны ўбачыць, што яго sequence number павялічыўся на адзінку - гэта значыць, што ён адправіў грошы нашаму акаўнту.
Застаўся апошні крок - загружаем код нашага кашалька (баланс яго ўжо папоўнены, але без кода смарт-кантракту мы не зможам ім кіраваць). Выконваем sendfile new-wallet-query.boc - І ўсё, у вас ёсць уласны кашалёк ў сетцы TON (хай і пакуль толькі тэставай).
Стварэнне выходных транзакцый
Каб пераводзіць грошы з балансу створанага акаўнта, ёсць файл crypto/block/wallet.fif, Які таксама трэба змясціць у тэчку з сабраным кліентам.
Аналагічна папярэднім крокам, у ім трэба паправіць суму, якую вы пераводзіце, адрас атрымальніка (dest_addr), і seqno вашага кашалька (ён роўны 1 пасля ініцыялізацыі кашалька і павялічваецца на 1 пасля кожнай выходнай транзакцыі - вы зможаце ўбачыць яго, запытаўшы стан свайго акаўнта) . Для тэстаў можаце выкарыстоўваць, напрыклад, мой кашалёк. 0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2.
Пры запуску (./crypto/fift wallet.fif) скрыпт возьме адрас вашага кашалька (адкуль вы перакладаеце) і яго прыватны ключ з файлаў new-wallet.addr и new-wallet.pk, а атрыманае паведамленне запіша ў new-wallet-query.boc.
Як і раней, каб непасрэдна выканаць транзакцыю, выклікаем sendfile new-wallet-query.boc у кліенце. Пасля гэтага не забываем абнавіць стан блокчейна (last) і правяраем, што баланс і seqno нашага кашалька змяніліся (getaccount <account_id>).
Вось і ўсё, зараз мы ўмеем ствараць смарт-кантракты ў TON і адпраўляць да іх запыты. Як бачым, цяперашняй функцыянальнасці ўжо дастаткова, каб, напрыклад, зрабіць больш прыязны кашалёк c графічным інтэрфейсам (зрэшты, чакаецца, што ён і так стане даступны як частка месэнджэра).
Толькі зарэгістраваныя карыстачы могуць удзельнічаць у апытанні. Увайдзіце, Калі ласка.
Ці зацікаўлены вы ў працягу артыкулаў з разборам TON, TVM, Fift?
Так, чакаю завяршэння цыкла артыкулаў з агульным аглядам TON
Так, цікава пачытаць падрабязней аб мове Fift
Так, хачу даведацца больш пра TON Virtual Machine і асэмблеры для яго