Больше года назад стало известно о планах мессенджера Telegram выпустить собственную децентрализованную сеть Telegram Open Network. Тогда стал доступен объемный технический документ, который, предположительно, был написан Николаем Дуровым и описывал структуру будущей сети. Для тех, кто пропустил — рекомендую ознакомиться с моим пересказом этого документа (часть 1, часть 2; третья часть, увы, всё ещё пылится в черновиках). С тех пор никаких значимых новостей о статусе разработки TON не было, пока пару дней назад (в одном из неофициальных каналов) не появилась ссылка на страницу https://test.ton.org/download.html, где размещены: ◦ ton-test-liteclient-full.tar.xz — исходники лёгкого клиента для тестовой сети TON;
◦ ton-lite-client-test1.config.json — конфигурационный файл для подключения к тестовой сети;
◦ README — информация о сборке и запуске клиента;
◦ HOWTO — пошаговая инструкция о создании смарт-контракта с помощью клиента;
◦ 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) вызываем команды: 

Для сборки интерпретатора языка Fift для смарт-контрактов (о нём ниже), также вызываем Скачиваем актуальный конфигурационный файл для подключения к тестовой сети и кладём его в папку с собранным клиентом. Готово, можно запустить клиент: Если всё сделано правильно, то вы должны увидеть что-то такое:  Доступных команд, как видим, немного: ◦ help — вывести этот список команд;
◦ quit — выйти;
◦ time — показать текущее время на сервере;
◦ status — показать состояние подключения и локальной БД;
◦ last — обновить состояние блокчейна (загрузить последний блок). Эту команду важно выполнять перед любыми запросами, чтобы быть уверенным, что вы видите именно актуальное состояние сети.
◦ sendfile <filename> — загрузить локальный файл в сеть TON. Так происходит взаимодействие с сетью — в том числе, например, создание новых смарт-контрактов и запросы на перевод средств между аккаунтами;
◦ getaccount <address> — показать текущее (на момент выполнения команды last) состояние аккаунта с указанным адресом;
◦ privkey <filename> — загрузить приватный ключ из локального файла. Если при запуске клиента передать ему папку с помощью опции -D, то он будет складывать в неё последний блок мастерчейна: Теперь можем перейти к более интересным вещам — изучить язык Fift, попробовать скомпилировать смарт-контракт (например, создать тестовый кошелёк), загрузить его в сеть и попробовать перевод средств между аккаунтами. Из документа fiftbase.pdf можно узнать, что для создания смарт-контрактов команда Telegram создала новый стековый язык Fift (видимо, от числительного fifth, по аналогии с Forth — языком, с которым у Fift много общего). Документ достаточно объемный, на 87 страниц, и я не стану подробно пересказывать его содержание в рамках этой статьи (как минимум, потому что сам не закончил его чтение :). Остановлюсь на основных моментах и приведу пару примеров кода на этом языке. На базовом уровне, синтаксис Фифта достаточно прост: его код состоит из слов, как правило, разделённых пробелами или переводами строк (частный случай: некоторые слова не требуют разделителя после себя). Любое слово — это регистро-зависимая последовательность символов, которой соответствует некоторое определение (грубо говоря, то, что интерпретатор должен сделать, когда встречает это слово). Если определения слова нет, интерпретатор пытается распарсить его как число и положить на стек. Кстати, числа тут — внезапно — 257-битные целые, а дробных нет совсем — точнее, они сразу превращаются в пару целых, образующих числитель и знаменатель рациональной дроби. Слова, как правило, взаимодействуют со значениями, лежащими на верхушке стека. Отдельный тип слов — префиксный — использует не стек, а последующие за ними символы из исходного файла. Например, так реализованы строковые литералы — символ «кавычка» (") является префиксным словом, которое ищет следующую (закрывающую) кавычку, и помещает строку между ними на стек. Подобным же образом ведут себя однострочные (//) и многострочные (/*) комментарии. На этом почти всё внутреннее устройство языка заканчивается. Всё остальное (включая управляющие конструкции) определено как слова (либо внутренние, такие как арифметические операции и определение новых слов; либо определённые в «стандартной библиотеке» Fift.fif, которая лежит в папке crypto/fift в исходниках). Простой пример программы на Fift: В первой строчке определяется новое слово setxy (обратите внимание на префикс {, который создает блок до закрывающего } и префикс :, который собственно определяет слово). setxy берёт число с вершины стека, определяет (или переопределяет) его как глобальную константу x, а квадрат этого числа — как константу y (учитывая, что значения констант можно переопределять, я бы скорее назвал их переменными, но я следую именованию в языке). В следующих двух строчках на стек кладётся число, вызывается setxy, затем выводятся значения констант x, y (для вывода используется слово .), обе константы помещаются на стек, суммируются и результат тоже выводится. В результате мы увидим: (Строчку «ok» выводит интерпретатор, когда заканчивает обрабатывать текущую строку в интерактивном режиме ввода) Ну и полноценный пример кода: Этот страшновато выглядящий файл предназначен для создания смарт-контракта — он будет помещён в файл new-wallet-query.boc после выполнения. Обратите внимание, что тут используется ещё один, ассемблерный язык для TON Virtual Machine (на нём я не буду останавливаться подробно), инструкции которого и будут помещены в блокчейн. Таким образом, ассемблер для TVM написан на Fift — исходники этого ассемблера находятся в файле crypto/fift/Asm.fif и подключаются в начале приведённого выше куска кода. Что я могу сказать, видимо, Николай Дуров просто любит создавать новые языки программирования :) Итак, предположим, мы собрали клиент TON и интерпретатор Fift, как описано выше, и познакомились с языком. Как теперь создать смарт-контракт? Об этом рассказывается в файлике HOWTO, приложенном к исходникам. Как я описывал в обзоре TON, эта сеть содержит больше одного блокчейна — есть один общий, т.н. «мастерчейн», а также произвольное количество дополнительных «воркчейнов», идентифицируемых 32-битным числом. Мастерчейн имеет идентификатор -1, кроме него так же может использоваться «базовый» воркчейн с идентификатором 0. У каждого воркчейна может быть своя конфигурация. Внутренне каждый воркчейн дробится на шардчейны, но это уже деталь реализации, которую необязательно держать в голове. В пределах одного воркчейна хранится множество аккаунтов, у которых есть свои идентификаторы account_id. Для мастерчейна и нулевого воркчейна они имеют длину 256 бит. Таким образом, идентификатор аккаунта записывается, например, так: Это «сырой» формат: сначала идентификатор воркчейна, затем двоеточие, и идентификатор аккаунта в шестнадцатеричной записи. Кроме того, есть укороченный формат — номер воркчейна и адрес аккаунта кодируются в бинарном виде, к ним дописывается контрольная сумма и всё это кодируется в Base64: Зная этот формат записи, мы можем запросить текущее состояние какого-нибудь аккаунта через тестовый клиент с помощью команды Получим примерно такой ответ: Видим структуру, которая хранится в 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, он выполнит его и завершится. Если имя файла опустить, то можно поиграть с интерпретатором в интерактивном режиме. В консоль после выполнения должно вывестись что-то такое: Это означает, что кошелёк с идентификатором -1:4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2 (или, что то же самое, 0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ) успешно создан. Соответствующий ему код окажется в файле new-wallet-query.boc, его адрес — в new-wallet.addr, а приватный ключ — в new-wallet.pk (будьте осторожны — повторный запуск скрипта перезапишет эти файлы). Конечно, сеть TON про этот кошелёк ещё не знает, он хранится только в виде этих файлов. Теперь его нужно загрузить в сеть. Правда, проблема в том, что для создания смарт-контракта нужно заплатить комиссию, а баланс у вашего аккаунта пока нулевой. В рабочем режиме эта проблема решится покупкой грамов на бирже (или переводом с другого кошелька). Ну а в нынешнем тестовом режиме заведён специальный смарт-контракт, у которого можно попросить до 20 грам просто так. Запрос к смарт-контракту, раздающему грамы налево и направо, делаем так. Во всё той же папке crypto/block находим файл testgiver.fif: Его тоже сохраним в папку с собранным клиентом, но поправим пятую строчку — перед строчкой "constant dest_addr". Заменим её на адрес того кошелька, который вы создали до этого (полный, не сокращённый). "-1:" в начале писать не нужно, вместо этого в начале поставьте "0x". Ещё можно поменять строку 6.666 Gram*/ constant amount — это сумма в грамах, которую вы запрашиваете (не больше 20). Даже если указываете целое число, оставьте десятичную точку. Наконец, нужно поправить строку 0x00000011 constant seqno. Первое число тут — это текущий sequence number, который хранится в аккаунте, выдающем грамы. Откуда его взять? Как говорилось выше, запустите клиент и выполните: В самом конце в данных смарт-контракта будет Число 0000000D (у вас оно будет больше) и есть sequence number, который надо подставить в testgiver.fif. Всё, сохраняем файл и запускаем (./crypto/fift testgiver.fif). На выходе получим файл wallet-query.boc. Это и есть сформированное сообщение к чужому смарт-контракту — просьба «переведи столько-то грам на такой-то аккаунт». С помощью клиента загружаем его в сеть: Если теперь вызвать 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 графическим интерфейсом (впрочем, ожидается, что он и так станет доступен как часть мессенджера).