Создание смарт-контракта в TON

cryptobro

New member
Сообщения
23
Реакции
6
Для начала у вас должен быть собран Light Client TON и интерпретатор языка Fift

Адреса и аккаунты в TON

Структура блокчейна 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]    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, ячейки — является древовидным, у каждой ячейки могут быть как свои данные, так и дочерние ячейки. Это показано в виде отступов в последних строчках.


Сборка смарт-контракта в TON

Создадим такую структуру самостоятельно (она называется BOC — bag of cells) с помощью языка Fift. В папке crypto/block из архива с исходниками есть файл new-wallet.fif, который поможет создать новый кошелёк. Скопируем его в папку с собранным клиентом. Как собрать клиент TON


Выполняем этот файл следующим образом:

./crypto/fift -I"<source-directory>/crypto/fift" new-wallet.fif

Здесь <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 не знает ничего об этом адресе. Его нужно загрузить в блокчейн TON. Также стоит понимать что для создания смарт-контракта нужно заплатить комиссию, а баланс у вашего аккаунта пока нулевой. В основной сети вы сможете для этого приобрести Gram на бирже или в обменниках или перевести с другого кошелька. В тестовой сети TON существует специальный кран (TON faucet), это смарт-контракт который даёт 20 GRAM на аккаунт при обращении к нему.


Формирование запроса к смарт-контракту TON faucet

Запрос к смарт-контракту TON faucet выполняется следующим образом. Во всё той же папке 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 — это сумма в Gram, которую вы запрашиваете (не больше 20). Даже если указываете целое число, оставьте десятичную точку.

Наконец, нужно поправить строку 0x00000011 constant seqno. Первое число тут — это текущий sequence number, который хранится в аккаунте, выдающем токены Gram. Откуда его взять? Как говорилось выше, запустите клиент и выполните:

Код:
last
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
В самом конце в данных смарт-контракта будет:

Код:
...
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}
Число 0000000D (у вас оно будет больше) и есть sequence number, который надо подставить в testgiver.fif.

Всё, сохраняем файл и запускаем (./crypto/fift testgiver.fif). На выходе получим файл wallet-query.boc. Это и есть сформированное сообщение к чужому смарт-контракту которая выглядит как запрос «перевод столько-то Gram на такой-то аккаунт».

С помощью TON Light Client загружаем файл wallet-query.bo в сеть:

Код:
> 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, а затем снова запросить статус аккаунта, у которого были запрошены Grams, то его 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>).

26


Выше вы увидели пример создания смарт-контракта в TON и взаимодействия с ним

За подготовку материала спасибо пользователю deNULL и порталу habr.com
 
Сверху