EOSDEV #2. Пишем Hello World смарт-контракт для EOS
Предыдущая статья
Теория
Так же, как и в Ethereum, разработчики сети EOS предлагают нам воспользоваться смарт контрактами для реализации логики запускаемых в ней приложений. Но в отличии от Ethereum, EOS не будет зацикливаться на какой то конкретной реализации виртуальной машины для запуска смарт контрактов. Спецификация предполагает, что изначально будут реализованы два варианта машины, но в дальнейшем их список может расшириться.
Первая предлагаемая виртуальная машина для запуска приложений будет использовать стандарт WebAssembly (WASM), который сейчас становится довольно популярным в веб-среде, позволяя разрабатывать высокопроизводительные приложения для работы в браузерах. С помощью небольших костылей (англ. - modifications) эти приложения можно будет ограничить от влияния внешнего мира и сделать детерминистическими, что позволит использовать их для имплементации смарт-контрактов. Хочу заметить, что команда Ethereum также работает над возможностью написания смарт-контрактов с помощью WebAssembly.
Второй вариант виртуальной машины, которую нам обещают реализовать - Ethereum Virtual Machine (EVM) из соответствующего проекта. Предполагается, что существующие смарт-контракты можно будет запустить в песочнице этой виртуальной машины без или с небольшими модификациями.
Для реализации нашего hello world
мы продолжим рассматривать WASM, т.к. пока не известно, когда будет реализована поддержка платформы EVM. Сейчас для разработки смарт-контрактов нам предлагается язык C/C++. Хипстерам думаю не очень понравится эта идея и они потребуют чего то более модного и высокоуровнего. Разработчики предусмотрели это и обещают реализовать поддержку чуть более высокоуровневых языков, таких как Rust, Solidity к лету 2018 года.
Практика
Генерим hello world
Для практики нам понадобится утилита eoscpp
, которая находится в папке ./build/tools
собранного проекта. Если вы не установили все собранные компоненты на системном уровне (make install
в папке ./build
), то рекомендую как минимум сделать это для утилиты eoscpp
:
cd ./build/tools/
make install
Теперь мы можем воспользоваться ей откуда угодно, чтобы создать новый проект под названием hello
:
eoscpp -n hello
В папке hello
мы обнаружим 3 файла:
hello.abi
- ABI интерфейс к бинарному контракту в формате JSON, аналогичный применяемому в Ethereum, но с другим синтаксисомhello.cpp
- исходный код контрактаhello.hpp
- хедер файл для исходного кода контракта
Заглянем в файл hello.cpp
:
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <hello.hpp>
/**
* The init() and apply() methods must have C calling convention so that the blockchain can lookup and
* call these methods.
*/
extern "C" {
/**
* This method is called once when the contract is published or updated.
*/
void init() {
eos::print( "Init World!\n" );
}
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t action ) {
eos::print( "Hello World: ", eos::Name(code), "->", eos::Name(action), "\n" );
}
} // extern "C"
Мы имеем 2 основные функции смарт контракта.
Первая из них, init()
, будет выполнена при деплое или передеплое контракта. Здесь необходимо выполнять первоначальную инициализацию стейта приложения.
Вторая это apply()
, которая по сути является обработчиком происходящих в сети событий. Разработчики также сравнивают ее с функцией main()
в обыкновенном приложении на C. Функция apply()
вызывается с двумя аргументами - code
и action
. Кодом, например, может быть контракт currency
, а в качестве действия вызываться transfer
. В паре, эти два аргумента образуют уникальный идентификатор события, происходящего в сети. Оно может быть отправлено определенной группе контрактов, в которую могут входить как получатели сообщения, так и его отправитель. Логика смарт-контракта должна сама определять - обрабатывать это событие или нет. В данном примере мы просто выводим в лог любые входящие значения аргументов code
и action
.
Деплоим
С помощью все той же утилиты eoscpp
, мы можем скомпилировать наш hello world пример в wast
файл:
eoscpp -o hello.wast hello.cpp
Далее нам понадобится запущенный демон ноды (eosd
), как его установить и запустить я описывал в предыдущем посте. С помощью консольного клиента (eosc
), мы создадим кошелек (wallet
) и импортируем первичный аккаунт (account
). Что значат эти термины в блокчейне EOS я опишу в ближайших постах:
eosc wallet create
eosc wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
Ключ 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
в последней строке является WIF ключом первичного аккаунта, который мы импортируем в кошелек. Взялся этот ключ из файла ./build/programs/data-dir/config.ini
. Последующие операции будем выполнять также от имени этого аккаунта.
Теперь, указав аккаунт, мы можем развернуть наш Hello World контракт:
eosc set contract inita hello.wast hello.abi
На что получим красноречивый json
ответ об успешном деплое контракта:
Reading WAST...
Assembling WASM...
Publishing contract...
{
"transaction_id": "01a7cb37a5632e563f7a271a8ecfda3e2191bfe73a76d2142db32427427832f0",
"processed": {
"refBlockNum": 283,
"refBlockPrefix": 3855761785,
"expiration": "2017-10-30T17:04:39",
"scope": [
"eos",
"inita"
],
"signatures": [
"20413f96262d6f2a39d76476994b756c197da730e62d2325ed78c57f0179fb5b0f58db691de1bf2ccec33bb6d9b14d8a36993aff00014b2883e65b861fd2d65fff"
],
"messages": [{
"code": "eos",
"type": "setcode",
"authorization": [{
"account": "inita",
"permission": "active"
}
],
"data": "000000000093dd740000f1010061736d0100000001110460017f0060017e0060000060027e7e00021b0203656e76067072696e746e000103656e76067072696e7473000003030202030404017000000503010001071903066d656d6f7279020004696e69740002056170706c7900030a20020600411010010b17004120100120001000413010012001100041c00010010b0b3f050041040b04504000000041100b0d496e697420576f726c64210a000041200b0e48656c6c6f20576f726c643a20000041300b032d3e000041c0000b020a000029046e616d6504067072696e746e0100067072696e7473010004696e697400056170706c790201300131010b4163636f756e744e616d65044e616d6502087472616e7366657200030466726f6d0b4163636f756e744e616d6502746f0b4163636f756e744e616d6506616d6f756e740655496e743634076163636f756e740002076163636f756e74044e616d650762616c616e63650655496e74363401000000572d3ccdcd087472616e7366657201000000204f4d11320369363401076163636f756e7401044e616d65076163636f756e74"
}
],
"output": [{
"notify": [],
"deferred_transactions": []
}
]
}
}
Особенности обработки транзакций
Если мы посмотрим в лог демона eosd
, то заметим, что фраза Init World
отобразилась 3 раза:
Это обусловлено логикой записи новой транзакции в блок. Я полагаю, что на сам стейт повлияет только одна из них. Официальная документация пока лишь дает поверхностную информацию о том, что так и должно быть:
- Первый раз
Init World
печатается нодой при запуске транзакции в песочнице, думаю это необходимо для валидации того, что все пройдет успешно - Второй раз
Init World
печатается нодой-продюсером блока при его сборке - Третий раз
Init World
печатается нодой при принятии блока от любого из продюсеров, в т.ч. и от себя
Не очень явно, но думаю, скоро нам дадут пояснения.
Отправляем сообщение нашему контракту
Теперь мы можем послать сообщение развернутому ранее контракту
eosc push message inita ping '"abcd"' --scope inita
Разберем команду более детально:
eosc push message
- отправить сообщение смарт-контрактуinita
- аккаунт, от которого отправляем сообщениеping
- действиеaction
'"abcd"'
- фейковые бинарные данные, в рабочем примере на их месте должен быть JSON, структура которого описывается в.abi
файле
В ответ получим JSON об успешной отправке сообщения:
{
"transaction_id": "c16bca4afb355238e19e56aeb36cca5cc2d9e0dd48fb4069affe470b59be2647",
"processed": {
"refBlockNum": 735,
"refBlockPrefix": 3432130105,
"expiration": "2017-10-30T17:29:48",
"scope": [
"inita"
],
"signatures": [],
"messages": [{
"code": "inita",
"type": "ping",
"authorization": [],
"data": "abcd"
}
],
"output": [{
"notify": [],
"deferred_transactions": []
}
]
}
}
Обратите внимание, что демон eosd
, также как и в случае с разворачиванием контракта, выводит в лог посланное нами сообщение 3 раза ¯¯\(ツ)/¯¯:
На сегодня все
Теперь мы можем смело бежать покупать токены EOS, чтобы меньше чем через год иметь возможность запустить свое Hello World приложение на реально работающей сети.
В следующем посте мы разберем, как модифицировать данный пример, добавив в него немного логики.