Больше денег: что такое Ethereum и как блокчейн меняет мир - Виталий Дмитриевич Бутерин
ВЫПОЛНЕНИЕ КОДА
Код, понимаемый контрактами Ethereum, пишется на низкоуровневом байткод-языке на основе стека, который мы назвали «код виртуальной машины Ethereum», или EVM-код. Код состоит из последовательности байтов, и каждый байт представляет какую-либо операцию. В целом выполнение кода представляет собой бесконечный цикл, состоящий из повторного выполнения операции текущего счетчика программ (начиная с 0) и последующего его увеличения на 1. Процесс может закончиться в случае достижения конца кода, ошибки в коде или обнаружения в коде инструкции STOP/RETURN. Есть три типа мест, где операции могут хранить данные:
◊ стек, контейнер типа last-in-first-out, куда можно записывать и откуда можно извлекать значения;
◊ память, байтовый массив произвольного размера;
◊ долговременное хранилище контракта, информация в котором хранится парами «ключ – значение». В отличие от стека и памяти, которые очищаются после выполнения кода, в хранилище параметры могут находиться долго.
Кроме того, код имеет доступ к сумме, отправителю и информации входящего сообщения, к информации в заголовке блока, а также может вернуть байтовый массив данных на выходе.
Принцип выполнения EVM-кода на удивление прост. Во время вычисления все текущее состояние представляет собой кортеж (block_state, transaction, message, code, memory, stack, pc, gas), где block_state – полное состояние, содержащее информацию о балансах и состояниях хранилищ всех аккаунтов. В начале каждого раунда выполнения берется pc-байткод (или 0, если pc >= len(code)), выполняется инструкция, соответствующая этому байту, и каждая из инструкций по-своему действует на кортеж.
Например, ADD достает два элемента из стека и возвращает туда их сумму, после чего уменьшает gas на 1 и увеличивает pc на 1; а SSTORE берет верхние два элемента стека и помещает второй элемент в хранилище контракта под адресом, равным первому элементу. Хотя есть много способов оптимизировать выполнение EVM посредством динамической компиляции, простейшая реализация Ethereum может занять всего пару сотен строк кода.
БЛОКЧЕЙН И МАЙНИНГ
Блокчейн Ethereum похож на блокчейн Bitcoin во многом, но не во всем. В первую очередь их архитектуры различаются тем, что в Ethereum, в отличие от Bitcoin, блоки содержат копию не только списка транзакций, но и последнего состояния системы. Также в них хранится еще два значения: номер блока и его сложность. Базовый алгоритм валидации блока в Ethereum выглядит так.
1. Убедиться, что блок, который определяется как предыдущий, существует и валиден.
2. Убедиться, что временная метка создающегося блока больше, чем у предыдущего, но не более чем на 15 минут.
3. Проверить валидность номера блока, его сложности, лимита газа, корень транзакции, корень анкла (различные низкоуровневые значения, специфичные для Ethereum).
4. Проверить валидность proof-of-work.
5. Пусть S[0] – состояние на конец предыдущего блока.
6. Пусть TX – список транзакций в блоке, а всего транзакций n. Для всех 0…n–1 установим S[i+1] = APPLY(S[i],TX[i]). Если при применении некоторых транзакций возникает ошибка или если общее количество потребленного газа к этому моменту превышает GASLIMIT, выдать ошибку.
7. Пусть S_FINAL будет S[n], но с выплатой майнеру награды за нахождение блока.
8. Проверить, совпадает ли корень дерева Меркла состояния S_FINAL с корнем финального состояния из заголовка блока. Если да, блок валиден; в противном случае – нет.
На первый взгляд такой подход может показаться крайне неэффективным, поскольку предполагает хранение текущего состояния в каждом блоке, но на самом деле его эффективность сопоставима с эффективностью Bitcoin. Причина в том, что состояния хранятся в древовидной структуре и с нахождением нового блока изменяется лишь малая часть дерева. Два смежных блока имеют по большей части одинаковые деревья, а значит, данные можно записать один раз, а потом на них ссылаться (например, через хеши поддеревьев). Для этого используется особая модификация дерева Меркла – «дерево Патрисии», которое позволяет эффективно добавлять и удалять ноды, а не просто менять их. Наконец, поскольку вся информация о состояниях содержится в последнем блоке, нет необходимости хранить всю историю блокчейна – в Bitcoin такая стратегия помогла бы сохранить в 5–20 раз больше пространства.
Часто возникает вопрос о том, «где» выполняется код контракта с точки зрения физического оборудования. На это есть простой ответ: процесс выполнения кода контракта – часть определения функции перехода состояния, которая, в свою очередь, является частью алгоритма валидации блока. Поэтому, если транзакция добавлена в блок B, выполнение кода, порожденного этой транзакцией, будет выполняться всеми нодами (и сейчас, и в будущем) при загрузке и валидации блока B.
ПРИЛОЖЕНИЯ
На базе Ethereum можно создать три типа приложений. Первая категория – финансовые приложения, позволяющие пользователям более эффективно заключать контракты на свои активы и управлять ими. Например, это подвалюты, финансовые деривативы, хедж-контракты, сберегательные кошельки, завещания и даже некоторые виды полноценных контрактов трудоустройства. Вторая категория – полуфинансовые приложения, где деньги также играют определенную роль, но далеко не главную. Идеальный пример – самоисполняемые награды за решение вычислительных задач. Наконец, есть полностью нефинансовые приложения вроде онлайн-голосования и децентрализованного управления.
СИСТЕМЫ ТОКЕНОВ
Системы токенов внутри блокчейна можно применять самыми разными способами: как подвалюты, представляющие активы вроде доллара США или золота; акции компаний; умную собственность; безопасные неподдельные купоны и даже системы токенов без привязки к какой-либо стоимости в ее привычном понимании, например мотивирующие системы оценки постов. Встроить системы токенов в Ethereum на удивление просто. Тут важно понимать, что и валюта, и система токенов – база данных с одной операцией: отнять х единиц от A и прибавить х единиц к B при условии, что, во-первых, сторона A имеет как минимум х единиц перед началом транзакции и, во-вторых, сторона A подтверждает транзакцию. Все, что нужно для реализации системы токенов, – встроить эту логику в контракт.
Простейший Serpent-код для осуществления системы токенов выглядит так:
def send(to, value):
if self.storage[msg.sender] >= value:
self.storage[msg.sender] = self.storage[msg.sender] – value
self.storage[to] = self.storage[to] + value
Это буквально функция изменения состояния «банковской системы», уже описанной выше. Нужно только добавить в начало несколько строк кода с исходным шагом распределения валюты и учесть еще несколько нюансов – в идеале неплохо бы также добавить функцию, позволяющую сторонним контрактам запрашивать информацию о балансе произвольного адреса. Но в целом это всё. Основанные на Ethereum системы токенов, используемые как подвалюты, теоретически обладают другой важной функцией, недоступной для метавалют на основе биткойна: возможностью платить комиссии непосредственно в этой валюте. Реализовать это