Преди повече от година стана известно за плановете на месинджъра Telegram да пусне собствена децентрализирана мрежа Telegram Open Network. Тогава стана достъпен обширен технически документ, който предполагаемо е написан от Николай Дуров и описва структурата на бъдещата мрежа. За тези, които пропуснаха — препоръчвам да се запознаят с моя резюме на този документ (, ; третата част, за съжаление, все още прашасва в черновите).
Оттогава не е имало значими новини относно статуса на разработката на TON, докато преди няколко дни (в един от ) не се появи линк към страницата , където са разположени:
◦ — изходни кодове за лек клиент за тестовата мрежа TON;
◦ — конфигурационен файл за свързване с тестовата мрежа;
◦ — информация за компилацията и стартирането на клиента;
◦ — стъпка по стъпка инструкция за създаване на смарт-контракт с помощта на клиента;
◦ — обновен документ (от 2 март 2019 г.) с технически обзор на мрежата TON;
◦ — техническо описание на TVM (TON Virtual Machine, виртуална машина TON);
◦ — техническо описание на блокчейна TON;
◦ — описание на новия език Fift, предназначен за създаване на смарт-контракти в TON.
Още веднъж, няма официални потвърждения за страницата и всички тези документи от страна на Телеграм, но обхватът на тези материали ги прави доста правдоподобни. Пускайте публикувания клиент на собствен риск.
Сборка на тестовия клиент
Нека първо опитаме да съберем и стартираме тестовия клиент — за щастие, подробно описва този несложен процес. Ще го направя на примера на macOS 10.14.5, не мога да гарантирам успешността на сборката на други системи.
Сваляме и разархивираме . Важно е да сваляте последната версия, тъй като обратно съвместимост на този етап не се гарантира.
Убедете се, че в системата са инсталирани най-новите версии на make, cmake (версии 3.0.2 или по-нови), OpenSSL (включително заглавни файлове C), g++ или clang. Нямаше нужда да инсталирам нищо допълнително, всичко се събра веднага.
Да предположим, че изходният код е разархивиран в папка
~\/lite-client. Отделно от нея създаваме празна папка за събрания проект (например,~\/liteclient-build), и от нея (cd ~\/liteclient-build) извикваме командите:cmake ~\/lite-client cmake --build . --target test-lite-client
За сборката на интерпретатора на езика Fift за смарт-контрактите (за него по-долу), също извиквамеcmake --build . --target fiftСваляме актуалния за свързване с тестовата мрежа и го поставяме в папката със събрания клиент.
Готово, може да стартирате клиента:
./test-lite-client -C ton-lite-client-test1.config.json
Ако всичко е направено правилно, трябва да видите нещо такова:

Достъпните команди, как видим, са малко:
◦ help — да изведете този списък с команди;
◦ quit — да излезете;
◦ time — да покажете актуалното време на сървъра;
◦ status — да покажете състоянието на връзката и локалната БД;
◦ last — актуализиране на състоянието на блокчейна (зареждане на последния блок). Тази команда е важна и трябва да се изпълнява преди всякакви запитвания, за да сте сигурни, че виждате именно актуалното състояние на мрежата.
◦ sendfile <filename> — зареждане на локален файл в мрежата TON. Така се осъществява взаимодействие с мрежата — включително, например, създаване на нови смарт-контракти и запитвания за прехвърляне на средства между акаунти;
◦ getaccount — да покаже текущото (в момента на изпълнение на командата last) състояние на акаунта с посочения адрес;
◦ privkey <filename> — зареждане на частния ключ от локален файл.
Ако при стартиране на клиента му предадете папка с опцията -D, то той ще съхрани в нея последния блок на мастерчейна:
.\/test-lite-client -C ton-lite-client-test1.config.json -D ~\/ton-db-dirСега можем да преминем към по-интригуващи неща — да изследваме езика Fift, да опитаме да компилираме смарт-контракт (например, да създадем тестов портфейл), да го заредим в мрежата и да опитаме прехвърляне на средства между акаунти.
Език Fift
От документа можем да научим, че за създаване на смарт-контракти екипът на Telegram е създал нов стеков език Fift (предположително, от числителното fifth, по аналогия с Forth — език, с който Fift има много общо).
Документът е доста обемен, с 87 страници, и няма да го преразказвам подробно в рамките на тази статия (понеже сам не съм го завършил :). Ще се спра на основните моменти и ще дам няколко примера за код на този език.
На базово ниво, синтаксисът на Фифт е достатъчно прост: неговият код се състои от думи, обикновено разделени с интервали или нови редове (изключение: някои думи не изискват разделител след себе си). Всяка дума — това е регистрозависима поредица от символи, на която съответства определено определение (грубо казано, това, което интерпретаторът трябва да направи, когато срещне тази дума). Ако определението на думата не съществува, интерпретаторът се опитва да я парсне като число и да я положи на стека. Между другото, числата тук са — изненадващо — 257-битови цели, а дробни няма überhaupt — по-точно, те веднага се преобразуват в двойка цели, образуващи числителя и знаменателя на рационалната дроб.
Думите обикновено взаимодействат със стойностите, лежащи на върха на стека. Отделен тип думи — префиксни — използва не стек, а следващите символи от изходния файл. Например, по този начин са реализирани строковите литерали — символът „кавычка“ (") е префиксна дума, която търси следващата (затваряща) кавичка и поставя строката между тях на стека. По подобен начин работят еднострочните (//) и многострочните (/*) коментари.
На това почти всичко вътрешно устройство на езика приключва. Всичко останало (включително управляващите конструкции) е определено като думи (или вътрешни, като аритметични операции и дефиниране на нови думи; или определени в „стандартната библиотека“ 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“ се извежда от интерпретатора, когато завърши обработката на текущия ред в интерактивен режим на въвеждане)
Ето и един пълен пример на код:
"Asm.fif" включва
-1 константа wc \/\/ създаване на портфейл в работната верига -1 (главна верига)
\/\/ Създайте нов прост портфейл
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF \/\/ върнете, ако recv_internal, провалете, освен ако recv_external
512 INT LDSLICEX DUP 32 PLDU \/\/ подпишите cs cnt
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS \/\/ подпишете cs cnt cnt' pubk
s1 s2 XCPU \/\/ подпишете cs cnt pubk cnt' cnt
EQUAL 33 THROWIFNOT \/\/ ( несъответствие seqno? )
s2 PUSH HASHSU \/\/ подпишете cs cnt pubk hash
s0 s4 s4 XC2PU \/\/ pubk cs cnt hash подпис pubk
CHKSIGNU \/\/ pubk cs cnt ?
34 THROWIFNOT \/\/ несъответствие на подписа
ACCEPT
SWAP 32 LDU NIP
DUP SREFS IF:<{
8 LDU LDREF // pubk cnt mode msg cs
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
}>
ENDS
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
}>c
\/\/ код
<b 0 32 u, newkeypair swap dup constant wallet_pk "new-wallet.pk" b>файл
B,
b> \/\/ данни
\/\/ без библиотеки
<b b{00110} s, rot ref, swap b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash dup constant wallet_addr
."new wallet address = " wc . .": " x. cr
wc over 7 smca>$ тип cr
256 u>B "new-wallet.addr" B>file
<b 0 32 u, b>
dup ."подписващо съобщение: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wc 8 i, wallet_addr 256 u, b{000010} swap <s b{0} b, b>
dup ."Външно съобщение за инициализация е " <s csr. cr
2 boc+>B dup Bx. cr
"new-wallet-query.boc" tuck B>file
."(Запазено в файл " тип .")" crТози изглеждащ заплашително файл е предназначен за създаване на смарт-контракт — той ще бъде поставен в файл new-wallet-query.boc след изпълнението. Обърнете внимание, че тук се използва и друг, асемблерен език за TON Virtual Machine (на който няма да спирам подробно), инструкциите на който ще бъдат поставени в блокчейн.
По този начин, асемблерът за TVM е написан на Fift — изходните файлове на този асемблер се намират в файла crypto/fift/Asm.fif и се свързват в началото на горепосочения код.
Какво мога да кажа, явно Николай Дуров просто обича да създава нови езици за програмиране 🙂
Създаване на смарт-контракт и взаимодействие с TON
И така, да си представим, че сме събрали клиента TON и интерпретатора Fift, както е описано по-горе, и сме се запознали с езика. Как сега да създадем смарт-контракт? За това става въпрос в файла , приложен към изходниците.
Акаунти в TON
Както описах в , тази мрежа съдържа повече от един блокчейн — има един общ, т.н. „мастерчейн“, както и произволно количество допълнителни „воркчейна“, идентифицирани с 32-битово число. Мастерчейнът има идентификатор -1, освен него може да се използва и „базов“ воркчейн с идентификатор 0. Всеки воркчейн може да има своя конфигурация. Вътрешно всеки воркчейн се разделя на шардчейни, но това е подробност от имплементацията, която не е нужно да държите в главата.
В рамките на един воркчейн се съхранява множество акаунти, които имат свои идентификатори account_id. За мастерчейна и нулевия воркчейн те имат дължина 256 бита. По този начин, идентификаторът на акаунта се записва, например, така:
-1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0dТова е „суровият“ формат: първо идентификаторът на воркчейна, след това двоеточието и идентификаторът на акаунта в шеснадесетичен запис.
Освен това, има съкратен формат — номерът на воркчейна и адресът на акаунта се кодира в бинарен вид, към тях се добавя контролна сума и всичко това се кодира в Base64:
Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkbЗнаейки този формат на записа, можем да поискаме текущото състояние на някой акаунт чрез тестовия клиент с помощта на командата
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0dЩе получим приблизително такъв отговор:
[ 3][t 2][1558746708.815218925][test-lite-client.cpp:631][!testnode] искам състоянието на акаунта за -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D
[ 3][t 2][1558746708.858564138][test-lite-client.cpp:652][!testnode] получих състоянието на акаунта за -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D относно блоковете (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F и (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F
състоянието на акаунта е (акаунт
адрес:(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.
Изпълняваме този файл по следния начин:
./crypto/fift -I"/crypto/fift" new-wallet.fifТук <source-directory> трябва да се замени с пътя до разопакованите изходни кодове (символът „~“ тук, за съжаление, не може да се използва, нужен е пълен път). Вместо да използвате ключа -I можете да определите променлива на средата FIFTPATH и да поставите този път в нея.
Тъй като Fift стартирахме с име на файл new-wallet.fif, той ще го изпълни и завърши. Ако пропуснете името на файла, можете да се поиграете с интерпретатора в интерактивен режим.
В конзолата след изпълнението трябва да се изведе нещо такова:
StateInit: x{34_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
нов адрес на портфейл = -1 : 4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ
подписване на съобщение: x{00000000}
Външно съобщение за инициализация е x{89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
B5EE9C724104030100000000D60002CF89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001001020084FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED5400480000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B6290698B
(Записан в файл 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+ "gift" $, b>
<b seqno 32 u, 1 8 swap ref, b>
дуп ."обгръщащо съобщение: " <s csr. cr
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 gram, b{00} s,
swap <s b>
дуп ."резултатно външно съобщение: " <s csr. cr
2 boc+>B дуп Bx. cr
"wallet-query.boc" B>файлНяма да го запазим в папка с компилиран клиент, но ще поправим петия ред — пред реда "constant dest_addr". Заместяме го с адреса на портфейла, който създадохте преди това (пълния, не съкратен). Не трябва да пишете "-1:" в началото, вместо това сложете "0x" в началото.
Може също да смените реда 6.666 Грама* / постоянна сума — това е сумата в грами, която искате (не повече от 20). Дори ако посочвате цяло число, оставете десетичната точка.
Накрая трябва да коригирате реда 0x00000011 постоянен seqno. Първото число тук е текущият номер на последователността, който се съхранява в акаунта, който раздава грамите. Как да го получите? Както е споменато по-горе, стартирайте клиента и изпълнете:
last
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0dв самия край на данните на смарт контракта ще бъде
...
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}Числото 0000000D (при вас то ще е по-голямо) е номерът на последователността, който трябва да бъде поставен в testgiver.fif.
Всичко, запазваме файла и стартираме (./crypto/fift testgiver.fif). На изхода получаваме файл wallet-query.boc. Това е съставеното съобщение к чужд смарт-контракт — молба „преведи толкова грам на такъв акаунт”.
С помощта на клиента го зареждаме в мрежата:
> sendfile wallet-query.boc
[ 1][t 1][1558747399.456575155][test-lite-client.cpp:577][!testnode] изпраща заявка от файла wallet-query.boc
[ 3][t 2][1558747399.500236034][test-lite-client.cpp:587][!query] статусът на външното съобщение е 1Ако сега извикаме last, а след това отново проверим статуса на акаунта, от който поискахме грамите, трябва да видим, че номерът на последователността му се е увеличил с единица — това означава, че е изпратил парите на нашия акаунт.
Остава последната стъпка — зареждаме кода на нашия портфейл (балансът му вече е попълнен, но без кода на смарт контракта не можем да го управляваме). Изпълняваме 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).

И това е всичко, сега можем да създаваме смарт договори в TON и да изпращаме към тях заявки. Както виждаме, текущата функционалност вече е достатъчна, за да направим например по-приветлив портфейл с графичен интерфейс (въпреки че се очаква, че той и без това ще бъде наличен като част от мессенджера).
Само регистрирани потребители могат да участват в анкетата. , моля.
Интересувате ли се от продължаване на статиите за TON, TVM, Fift?
Да, очаквам завършването на цикъла статии за общия преглед на TON
Да, интересно ми е да прочета повече за езика Fift
Да, искам да узная повече за TON Virtual Machine и асемблера за него
Не, не ме интересува нищо от това
Гласували са 39 потребители. Въздържали се 12 потребители.
Какво мислите за плановете на Telegram за стартиране на TON?
Имам големи надежди за този проект
Просто следя развитието му с интерес
Настроен съм скептично, съмнявам се в успеха му
Склонен съм да считам тази инициатива за неуспешна, ненужна на широката публика
Гласували са 47 потребители. Въздържали се 12 потребители.
Източник: habr.com

