Сергей Лоншаков
Разработчик Blockchain решений, Team Lead, Airalab
http://aira.life/
Выступление на Microsoft Blockchain Intensive Day 2
Сергей активно занимается исследованием и разработкой таких проектов:
- Bitcoin/Altcoin/криптовалютные проекты с 2011.
- Smart contract/Ethereum проекты с 2014.
- DAO (Децентрализованные Автономные Организации) с 2015.
- Децентрализованный интернет вещей с осени 2015 года.
- Экономика роботов с 2016 года.
C 2011 года я занимаюсь технологией блокчейн, начав с биткоина. Занимались аутсорсингом. В 2013 году был Research Develop, познакомились с Codius, с альтчейнами, с работами Niko Zabo. Через работы Niko Zabo увидели публикации Виталика Бутерина, познакомились с эфиром. Начали работать с момента ICO эфира с комьюнити, начали пробовать делать первые контракты. Самый первый контракт писали не мы, а вообще всё сообщество писало первый контракт примерно полгода.
После официального запуска сети Ethereum два года назад всё ускорилось. Мы начали более менее уметь писать контракты, появились библиотеки, начал доминировать язык Solidity, то есть контракты можно писать было на разных языках. Но Solidity выиграл, пока что не вижу, чтобы другие альтернативные языки получили сильную поддержку в сообществе.
И сейчас есть уже определённые наработки, которыми я хочу поделиться. Но самая главная сейчас наша задача: разобраться всё таки в том, что такое сеть Ethereum, что такое виртуальная машина в сети Ethereum.
Децентрализованный компьютер
Итак, децентрализованный компьютер Ethereum. Что такое Virtual Machine. Если очень абстрактно, это просто часть протокола. Это модуль протокола, который выполняет определённые вычисления на локальной машине. Вторая формулировка, которую чаще всего любят использовать в СМИ, это единый глобальный децентрализованный компьютер, который невозможно остановить. В среде разработчиков это штука, с помощью которой можно делать децентрализованные приложения.
Очень хорошо рассказывает про Ethereum Machine Гэвин Вуд в первом своём выступлении на Devcon1, который проходил в Лондоне в 2015 году. И, самое основное, чем заканчивал Гэвин Вуд своё выступление, что Ethereum Virtual Machine — это не очень хороший компьютер, медленный и очень дорогой. По стоимости использования, по затратам ресурсов, по сложности реализации где-то в районе середины 20 века.
Выступление Гэвина Вуда на Devcon1
Тогда встаёт вопрос: зачем он нужен?
Ответ на это простой: это действительно первый единый мировой компьютер. Аналогово пока что нет.
Второе, то, что вы делает с EVM, не может быть выключено централизованно, то есть, когда вы используете сеть Ethereum, вы должны понимать, что то, что вы там поселите, останется там на веки вечные.
И, философское: EVM - вездесущий, доступный повсюду, где есть интернет. Из-за того, что исходный код открыт, мы находимся сейчас в Киеве, вы используете open source и создаёте свои виртуальные машины. Вы можете использовать публичный или приватный блокчейн, но по сути, вы будете создавать децентрализованный компьютер, просто скачав исходный код и запустив у себя. Так делают люди в разных концах мира. Доступ к нему есть у каждого в любом компьютере мира.
И это очень многое меняет. Потому что чтобы в принципе подключиться к каким-либо системам удалённо, нужна идентификация, необходимо кому-то доверять. Здесь нет. Вы его скачали, развернули и он работает в децентрализованном режиме без необходимости полагаться на кого-то ещё.
Почему Ethereum ещё популярен
Вокруг Ethereum протокола формируется очень хорошее устойчивое сообщество. И технологии, которыми обрастает Ethereum протокол, очень популярны.
Solidity является родственным JavaScript, очень любят разработчики использовать HTML5. Очень многие популярные веб-технологии близки и родны Ethereum-протоколу. Поэтому, начиная работать с ним, вы легко можете найти людей, которые быстро смогут мигрировать технически и начать разрабатывать. Единственная сложность, с которой вы столкнётесь — концептуальная. В архитектуре этой виртуальной машины. Поэтому мы погрузимся в саму виртуальную машину.
Ethereum Virtual Machine и Тьюринг полнота
EVM поддерживает Тьюринг полноту, поэтому на ней может быть создано что угодно, написан любой код. То есть, EVM не ограничивает ваши архитектурные возможности. Вы можете запрограммировать любой процесс и он будет использован в Virtual Machine.
Но есть ограничения, которые вызваны именно существование в децентрализованной среде. Чтобы к ним перейти, разберёмся с понятием Аккаунты.
Ethereum Virtual Machine и умные контракты
В сети Ethereum есть два типа аккаунтов.
- Externally owned account, EOAs. Это обычный аккаунт пользователя. Это аккаунты, в которых, когда вы скачиваете протокол и создаёте локальный приватный ключ, у вас генерируется локальный аккаунт.
- Контракт. Это учётная запись, которая обладает выделенной памятью, собственным кодом и управляется с помощью кода.
И контракты и аккаунты могут хранить эфиры, иметь на своих балансах какие-либо внутренние токены или эфир. Но контракт не может самостоятельно исполняться без EOAs. То есть, контракты могут обращаться к другим контрактам, но это называется internal transaction, внутренняя транзакция, и она порождается только внешним аккаунтом.
Если никто не отправляет EOAs транзакции в сеть Ethereum, то внутри, по сути, компьютер мёртв. Он не жив. Ничего не происходит. Только EOAs оживляют эту виртуальную машину, приводят её в действие и вызывают контракты. Что вы можете что сделать: отправить транзакцию с одного EOAs на другой, так же как в любой криптовалютной сети, также отправляются биткоины. Второе, вы можете обратиться к контракту, вызвав его функцию и в этой функции, например, уже внутри может вызываться другой контракт. Но это может произойти исключительно только если EOAs вызвал начальный контракт.
Это очень важно понять. Потому что иногда допускают ошибки, думая, что я сейчас сделаю контракт, который смотрит во внешний мир, что-то там находит, берёт оттуда данные, сам в себя их поселяет и нас уведомляет. Нет, так нельзя.
Анатомия умного контракта: bytecode
Контракты в Ethereum Blockchain живут в бинарном виде (EVM bytecode). То есть их пишут на языке высокого уровня (Solidity), компилируют, получают машинно-независимый код низкого уровня (bytecode) из исходного кода.
Именно bytecode сохраняется в Ethereum Blockchain. Там не хранится исходный код контракта. Есть декомпиляторы, их пишут понемногу. Но исходного кода в Ethereum Blockchain нет.
Подготовленный bytecode загружается в сеть Ethereum с помощью отправки команды "Создание контракта" обычным клиентом сети. После этого bytecode создаст за собой уникальный адрес.
И контракт и EOA по адресу неотличимы. Если вам отправляют адрес и вы не посмотрите внутрь блокчейна, вы не будете знать: EOA это или контракт. Но это можно легко понять, заглянув в блокчейн. Если это контракт, то там будет транзакция на создание, т.е. самое первое, с чего начинает отражаться контракт в Ethereum Blockchain, это с транзакции с EOA типа Creation Contract, и у контракта появляется адрес. Увидев такую транзакцию, вы понимаете, что это контракт.
Анатомия умного контракта: abi
Когда у вас есть исходный код, вы его компилируете, получается bytecode и он сохраняется в блокчейне. Некоторые думают, что сейчас они введут этот адрес в строку и сразу же могут начать с ним работать. Это не так. Интерфейс к контракту, к этому уже сохранённому байткоду не хранится в блокчейне Ethereum. То есть, когда вы компилируете контракт, у вас появляется переменная с байткодом и abi (Application Binary Interface, бинарный интерфейс приложения). Это как раз таки интерфейс. Его необходимо передать пользователю вашего контракта. Либо как-то вручную, как в самом начале: переслать друг другу файлы, подгружать их и уже работать с контрактами.
То есть, вы указываете адрес и abi к нему. И только после этого вы сможете прочитать контракт, чтобы понять какие в нём функции и переменные, прочитать его содержимое. Для вашего проекта abi вы должны зашивать в HTML или где-то на серверной логике. То есть вы его должны хранить у себя вне блокчейна в своём проекте. Это тоже очень важно учитывать.
Анатомия умного контракт: gas
Gas — это метрика выполняемых вычислений и операций сохранения данных в Ethereum Blockchain.
Для чего он существует? Сеть Ethereum не может быть сильнее по вычислениям, чем мощность среднего компьютера, находящегося в сети. Потому что каждый участник сети перевыполняет все вычисления. Представим, что у нас есть безлимитное количество возможных вычислений. Например, я напишу контракт с бесконечным циклом. В результате, если не будет ограничения по возможности максимальных вычислений, будет перевыполнение памяти и выключатся ноды. Примерно такая ситуация была на Devcon2 в Шанхае в первый день, когда все собрались на первой лекции, кто-то нашёл интересную ошибку: она не газ преодолевала, а просто вызывала такие вычисления на машине, что было переполнение памяти и машины в первый день конференции начали выключаться, переполняться.
Чтобы такой проблемы не было, чтобы сеть после первого контракта с бесконечным циклом не разрушилась, была придумана внутренняя метрика, система ограничения, защиты, безопасности этого единого мирового компьютера от того, чтобы не вырубить участников сети - Gas.
Gas имеет экономический стимул. В биткоине, например, вы платите в биткоинах. Там фиксированная сумма. Вы можете её поменять и повысить себя в ранжировании именно на проведение транзакции. Почему там просто? Потому что все транзакции в сети биткоин имеют одинаковую сложность вычисления. Мы всегда знаем сколько стоит переслать по вычислительным мощностям одного участника сети, биткоин с одного компьютера на другой компьютер. В Ethereum люди могут писать любые контракты с любой логикой и участники сети не знают, в конечном итоге, что именно контракт будет делать. Контракты могут появляться с абсолютно разнообразными выполняемыми функциями.
И чтобы каким-то образом защитить компьютер и создать систему, в которой вычисление будет что-то стоить, был придуман газ.
Все вычисления (об этом можно прочитать в Yellow Paper Гэвина Вуда) имеют свою сложность. Каждая операция в этом бинарном формате имеет свою сложность вычисления и в зависимости от сложности вычисления была составлена таблица сопоставления сложности операции и требуемого количества газа. И, при написании контракта, когда он превращается в байткод, можно посмореть этот список опкодов, которые он должен выполнять. И из этих параметров, количества выполняемых опкодов, формируется стоимость в газе: сколько вычислений в этой внутренней метрике EVM требуется, чтобы выполнить ту логику, которая заложена в контракте.
Хорошо, что заранее, когда вы напишете свой контракт, вы можете узнать и просчитать расход газа. Очень советую, когда будете делать пилотный проект и попытаетесь его реализовать, выходить в продакшн, обязательно составьте таблицу, в которой есть каждая функция и сколько она газа ест. Ещё важно, чтобы себя защитить, провести стресс-тестирование и каждую функцию по раз десять вызвать и посмотреть методом тыка, где можно заметить, растёт ли расход газа при каждом новом использовании контракта или нет. Если в умном контракте будут функции, которые с каждым использованием будут увеличивать количество требуемого газа, убирайте эти функции и пересматривайте их. Потому что они будут нежизнеспособны.
Как пример, на Ethereum запускался SatoshiDice, обычный гемблинг-проект. Работал условный рандом и люди либо выигрывали либо проигрывали. У ребят был цикл по увеличивающемуся количеству ставок. И в какой-то момент у них просто получилось так, что количество операций проходов в цикле дошло до общего лимита на один блок в сети Ethereum и на контракте зависло несколько тысяч эфиров. Чтобы такого не произошло, делаем таблицу с расходом газа, 10 раз создаём контракт, 10 раз используем каждую функцию, которая в нём есть. Смотрим: растёт ли газ? Если растёт — пересматриваем функцию, исправляем её. Если не растёт - то первую ошибку преодолели.
Механизм ранжирования
Gas не равно fee. Gas — это не сразу сколько вы должны в эфирах. Для того, чтобы реализовать механизм ранжирования, у нас есть ограниченный ресурс в виде объёма газа в одном блоке и неограниченное, неопределённое количество людей, которые хотят воспользоваться этим блоком, чтобы поселить там свою транзакцию.
Есть газ, метрика, которая привязана к количеству выполняемых вычислений. Требуется реализовать ранжирование, приоритетность. Можно было бы считать, кто первый отправил, но это не работает в децентрализованных сетях, когда несколько людей майнят блоки и это не имеет экономического стимула.
Когда отправляете транзакцию, вы можете указать сколько вэй (копеек Ethereum) готовы заплатить за одну единицу газа. Это полезно сразу понимать в вашем проекте, чтобы реализовывать механизм гарантированного поселения в блоке.
Во время Devcon2 сеть начали ддосить с выводом из строев нод, была выпущена заплатка и снижен лимит газа в блоке с 3 до 1 миллиона. Одна простая транзакция с аккаунта на аккаунт стоит 22000 газа. И иногда уже не хватало, блоки были переполнены. И, чтобы гарантировать попадание в блок, биржи стали платить больше вэй за единицу газа. То есть, когда транзакции приходят к майнерам, они ранжируются по количеству газа, который готовы заплатить за единицу вычислений.
Если важно попадание транзакций в блок, не обязательно максимально повышать оплату за единицу газа, а значит, что надо написать простого демона, который будет анализировать объём газа в блоке и смотреть среднюю цену в вэй, это можно увидеть, смотря каждый блок. И, когда понимаете, что места не хватает в блоке — повышаете автоматически в отправляемой транзакции количество вэй на единицу газа.
Чем сложнее контракт, чем больше он сохраняет, тем дороже это будет обходиться.
Пример с бесконечным циклом
Если вы сделаете бесконечный цикл в вашем умном контракте, то это не значит, что майнеры откажутся вашу транзакцию проводить. Они её проведут, только следующем образом.
Как работает блок у майнера? Приходят транзакции, он их ранжирует, проводит вычисления, забирает комиссию за вычисления. И, если вы отправили транзакцию, которая требует больше газа, чем есть свободного для неё в блоке, даже представим, что нет никого, кто в этом блоке отправляет транзакции, сейчас там четыре с небольшим миллиона газа лимита. Можно поделить на 22000 и посчитать, сколько простых операций в 12-15 секунд можно там отправить. Сколько простых транзакций можно сделать в один блок. Средний контракт желательно придерживать, чтобы функция не выходила за 200000 - 500000 газа. Сегодня такое среднее значение. По практике знаю, что написать контракт с функцией, которая съедает меньше 200000 газа, это будет очень простая функция. Если хотите что-нибудь среднее сделать, то это 200000-500000. 500000 - это создание контракта.
Представим, что никого нет и есть 4 миллиона газа, лимит в блоке, и мы запускаем контракт с бесконечным циклом. Что произойдёт: майнер получит этот контракт, выполнит в пределах своего лимита в блоке, прокрутит его много раз, возьмёт fee за это и обрубит его на 4 миллионах газа, напишет out of gas. Ничего не произведёт с внутренним хранилищем контракта, вся транзакция будет развёрнута, но за вычисления, которые он произвёл, чтобы дойти до состояния реверт, он возьмёт комиссию и пометит out of gas.
Майнер локально может изменять лимит газа максимум на 0.0976% в один блок. То есть, когда с выходом заплатки или новой версии изменяется лимит газа, он поднимается не сразу, как все майнеры себе поставили, а изменяется в сторону увеличения или уменьшения с каждым найденным блоком на 1/100 процента. Это позволяет переконфигурировать нагрузку на сеть.
Итак, газ это метрика. Это не напрямую fee. За единицу газа вы указываете, сколько готовы заплатить за условную единицу вычисления этого глобального компьютера сети Ethereum уже во внутренней валюте. Всё можно посчитать заранее и обязательно нужно посчитать. У блоков есть лимит газа. Его можно повышать майнерами, например, в приватной сети можно поиграться. При нахождении каждого нового блока газ может увеличиваться или уменьшаться в сети на определённую константу.
Вот так в консоли выглядит контракт в уже скомпилированном виде.
Первый код в таком виде хранится. Дальше можно посмотреть информацию: версия компилятора, язык, на котором он скомпилирован, и abi в JSON-формате. Соответственно, код идёт в Ethereum Blockchain, abi вы должны отдельно сохранить.
Инструменты для работы с Ethereum Blockchain
Первый клиент: Geth
https://github.com/ethereum/go-ethereum
Есть Ethereum-протокол, а есть много разных клиентов. Самый первый клиент, который запустился во Frontier Release, это Geth.
Его много кто использует, это интерфейс к Ethereum-протоколу командной строки. С ним удобно работать. Нормальная консольная утилита. На языке Go, что тоже добавляет ему плюсов.
Выпущено 118 релизов, начиная с 8 февраля 2014 года по 5 июля 2017 года. Что даёт уверенности в надёжности клиента.
Возможности использования Geth: interfaces & API
JavaScript Console
Библиотека web3 JavaScript Dapp API
JSON RPC API
JSON RPC server
Command line options
Пример отправки контракта в сеть Ethereum
Parity
Есть 2 типа клиента: для работы с публичной нодой Ethereum и для Proof of authority, то есть для того, чтобы поднять свою сетку с выделенными узлами, которые наделены правами вмешательства в дела блокчейна.
Реализован на языке Rust
На Parity находится 2/3 - 3/4 майнеров. Он работает стабильно. Но, опять же, Geth - это не плохо, он тоже очень хорош.
Parity - это детище Гэвина Вуда, команды Ethcore. Когда Гэвин Вуд отделился от Виталика Бутерина, он начал заниматься Ethcore. Выпускается с 2016 года, очень активно разрабатывается.
Parity: interfaces & API
Parity имеет режим совместимости с geth, но он не работает
JSON RPC API
parity JavaScript Dapp API (+web3)
web3 JavaScript Dapp API
Ethereumj
Ethereumj является разработкой команды Hack.ether.camp
Если есть интерес к клиенту на Java, можно взять и дописывать.
С 15 сентября 2014 года выпущено 33 релиза.
Ethereum Wallet & Mist
Это прикладные решения. Через них работают пользователи.
Ethereum Wallet уже немножечко прошлое. Разработчики Ethereum Wallet и Mist ушли в Mist.
The Mist - это браузер со встроенной библиотекой web3, который понимает локально запущенную geth-ноду, она с ней идёт в комплекте. То есть, когда запускаете Mist, запускается geth локально. И он к нему коннектится.
Когда вы заходите на сайт, в котором есть поддержка web3, ваше Dapp, к примеру. Он сразу же в браузере выводит балансы, то есть вы сразу можете воспользоваться всеми возможностями, которые есть у Ethereum-протокола, находящегося на машине пользователя.
Платформа написания: Meteor + Electron, не советую. Сложно и запутано.
MetaMask
Плагин для браузера Google Chrome, позволяющий запускать Dapp без полной ноды на клиенте.
Когда вы просите клиента поставить MetaMask, у него генерируется локальный приватный ключ, вы отправляете ему RPC-канал, к которому подключаться и всё. Он в вашей инфраструктуре. Не требуется выкачивать весь блокчейн.
Общается с полной нодой через JSON RPC API.
Написан на JavaScript.
Пример умного контракта
Есть токен. У токена можно сделать функцию эмиссии. Можно указать: какой адрес может выполнять эту эмиссию и сделать функцию, которая может сменить того, кто может указывать эту эмиссию.
Ещё интереснее сценарий, например, энергетической единой системы, где нужна внутренняя единица. Access Control List. То есть, у нас есть умный контракт, в котором можно создавать группы и указывать адреса тех, кто к этой группе относится. И назначить эмитентом не конкретный адрес-аккаунт, а ACL. Тогда можем сделать, например, смарт-грид солнечных панелей и каждая панель, вырабатывая энергию, вырабатывает токен.
Такой же принцип можно применить к множеству проектов, где нам интересно было бы рассмотреть эмиссию токенов не одним конкретным узлом, а множеством участников.
Потому что я видел ещё такие проекты, в которых нужно эмиссировать множество разных участников, но они просто берут и делают сервер, на который они какими-то ещё дополнительными средствами безопасности, отдельными ключами и прочим, подключаются, их там перепроверяют, и этот ключ эмиссирует токен. Это бессмысленно делать, лучше сделать сразу же группу ACL и в него добавлять адреса тех, кто может проводить эмиссию.
Репозиторий Airalab на Github
https://github.com/airalab/core есть все контракты. Они разделены по типу.
В директории token есть несколько видов токенов:
Токен без эмиссии, который вы один раз создаёте, делаете ограниченную эмиссию один раз и потом с ней играетесь.
TokenEmission, в котором можно назначить того, кто делает эмиссию.
TokenEmissionACL, токен с ACL.
Три вида токенов есть, которые можно брать для примера и пробовать с ними работать.
В директории foundation есть контракт Congress. Например, если нужно реализовать систему голосования.
В директории market есть пример умного контракта рынка, который умеет работать с токенами.
Презентация с другого события, но в основном слайды те же:
https://www.slideshare.net/EnsRationis2020/ethereum-virtual-machine