Основы работы Ethereum

Введение

Скорее всего, вы слышали о блокчейне Ethereum, независимо от того, знаете ли вы, что это такое. В последнее время это было много в новостях, в том числе на обложках некоторых крупных журналов, но чтение этих статей может быть похоже на тарабарщину, если у вас нет основы для того, что именно такое Ethereum. Так что же это такое? По сути, публичная база данных, которая ведет постоянный учет цифровых транзакций. Важно отметить, что эта база данных не требует какого-либо центрального органа для ее обслуживания и защиты. Вместо этого он работает как «не доверяющая» транзакционная система — структура, в которой люди могут совершать одноранговые транзакции без необходимости доверять третьей стороне ИЛИ друг другу.

Все еще в замешательстве? Вот тут-то и появляется этот пост. Моя цель состоит в том, чтобы объяснить, как Ethereum функционирует на техническом уровне, без сложной математики или пугающих формул. Даже если вы не программист, я надеюсь, что вы уйдете, по крайней мере, с лучшим пониманием технологии. Если некоторые детали слишком техничны и их трудно нащупать, это совершенно нормально! На самом деле нет необходимости понимать каждую мелочь. Я рекомендую просто сосредоточиться на понимании вещей на широком уровне.

Многие из тем, затронутых в этом посте, представляют собой разбивку концепций, обсуждаемых в желтом документе. Я добавил свои собственные объяснения и диаграммы, чтобы облегчить понимание Ethereum. Те, кто достаточно смел, чтобы принять технический вызов, также могут прочитать желтую бумагу Ethereum.

Определение блокчейна

Блокчейн — это «криптографически безопасная транзакционная синглтонная машина с общим состоянием». [1] Это глоток, не так ли? Давайте разберемся.

  • «Криптографически безопасная» означает, что создание цифровой валюты защищено сложными математическими алгоритмами, которые неприлично трудно взломать. Подумайте о своего рода брандмауэре. Они делают практически невозможным обман системы (например, создание поддельных транзакций, удаление транзакций и т. Д.).
  • «Транзакционная синглтонная машина» означает, что существует один канонический экземпляр машины, ответственный за все транзакции, создаваемые в системе. Другими словами, есть единая глобальная истина, в которую все верят.
  • «С общим состоянием» означает, что состояние, хранящееся на этой машине, является общим и открытым для всех.

Ethereum реализует эту парадигму блокчейна.

Объяснение парадигмы блокчейна Ethereum

Блокчейн Ethereum — это, по сути, конечный автомат на основе транзакций. В информатике конечный автомат относится к чему-то, что будет считывать серию входных данных и, основываясь на этих входах, перейдет в новое состояние.

С конечного автомата Ethereum мы начинаем с «состояния генезиса». Это аналогично чистому листу, до того, как в сети произошли какие-либо транзакции. Когда транзакции выполняются, это состояние генезиса переходит в некоторое конечное состояние. В любой момент времени это конечное состояние представляет текущее состояние Ethereum.

Состояние Ethereum имеет миллионы транзакций. Эти транзакции сгруппированы в «блоки». Блок содержит серию транзакций, и каждый блок связан вместе со своим предыдущим блоком.

Чтобы вызвать переход из одного состояния в другое, транзакция должна быть действительной. Чтобы транзакция считалась действительной, она должна пройти процесс проверки, известный как майнинг. Майнинг — это когда группа узлов (т.е. компьютеров) расходует свои вычислительные ресурсы для создания блока допустимых транзакций.

Любой узел в сети, который объявляет себя майнером, может попытаться создать и проверить блок. Многие майнеры со всего мира пытаются создавать и проверять блоки одновременно. Каждый майнер предоставляет математическое «доказательство» при подаче блока в блокчейн, и это доказательство выступает в качестве гарантии: если доказательство существует, блок должен быть действительным.

Чтобы блок был добавлен в основной блокчейн, майнер должен доказать это быстрее, чем любой другой майнер конкурента. Процесс проверки каждого блока путем предоставления майнером математического доказательства известен как «доказательство работы».

Майнер, который проверяет новый блок, вознаграждается определенной суммой стоимости за выполнение этой работы. Что это за ценность? Блокчейн Ethereum использует встроенный цифровой токен под названием «Ether». Каждый раз, когда майнер доказывает блок, генерируются и награждаются новые токены Ether.

Вы можете задаться вопросом: что гарантирует, что все придерживаются одной цепочки блоков? Как мы можем быть уверены, что не существует подмножества майнеров, которые решат создать свою собственную цепочку блоков?

Ранее мы определили блокчейн как транзакционную синглтонную машину с общим состоянием. Используя это определение, можно понять правильное текущее состояние – это единая глобальная истина, которую каждый должен принять. Наличие нескольких состояний (или цепочек) разрушило бы всю систему, потому что было бы невозможно договориться о том, какое государство является правильным. Если цепи будут расходиться, вы можете владеть 10 монетами на одной цепочке, 20 на другой и 40 на другой. В этом сценарии не будет никакого способа определить, какая цепочка является наиболее «действительной».

Всякий раз, когда генерируется несколько путей, происходит «форк». Обычно мы хотим избежать форков, потому что они разрушают систему и заставляют людей выбирать, в какую цепочку они «верят».

Чтобы определить, какой путь является наиболее допустимым, и предотвратить несколько цепочек, Ethereum использует механизм, называемый «протоколом GHOST».

“GHOST” = “Greedy Heaviest Observed Subtree”

Проще говоря, протокол GHOST говорит, что мы должны выбрать путь, на котором было выполнено больше всего вычислений. Одним из способов определения этого пути является использование номера блока самого последнего блока («листового блока»), который представляет собой общее количество блоков в текущем пути (не считая блока генезиса). Чем выше номер блока, тем длиннее путь и тем больше усилий по добыче, которые должны были быть потрачены на достижение листа. Использование этого рассуждения позволяет договориться о канонической версии текущего состояния.

Теперь, когда вы получили 10 000-футовый обзор того, что такое блокчейн, давайте углубимся в основные компоненты, из которых состоит система Ethereum:

  • счета
  • государство
  • газ и сборы
  • операций
  • блоки
  • выполнение транзакций
  • добыча
  • подтверждение работы

Одно замечание перед началом работы: всякий раз, когда я говорю «хэш» X, я имею в виду хэш KECCAK-256, который использует Ethereum.

Счета

Глобальное «общее состояние» Ethereum состоит из множества небольших объектов («учетных записей»), которые способны взаимодействовать друг с другом через структуру передачи сообщений. Каждая учетная запись имеет связанное с ней состояние и 20-байтовый адрес. Адрес в Ethereum — это 160-битный идентификатор, который используется для идентификации любой учетной записи.

Существует два типа учетных записей:

  • Внешние учетные записи, которые контролируются закрытыми ключами и не имеют связанного с ними кода.
  • Контрактные счета, которые контролируются их контрактным кодом и имеют код, связанный с ними.

Счета, находящиеся в внешнем владении, в сравнении с контрактными счетами

Важно понимать фундаментальную разницу между внешними учетными записями и контрактными счетами. Внешняя учетная запись может отправлять сообщения другим внешним учетным записям ИЛИ другим учетным записям контракта, создавая и подписывая транзакцию с использованием своего закрытого ключа. Сообщение между двумя внешними учетными записями — это просто передача стоимости. Но сообщение с внешней учетной записи на контрактную учетную запись активирует код контрактной учетной записи, позволяя ей выполнять различные действия (например, переводить токены, записывать во внутреннее хранилище, чеканить новые токены, выполнять некоторые вычисления, создавать новые контракты и т. Д.).

В отличие от внешних счетов, контрактные счета не могут инициировать новые транзакции самостоятельно. Вместо этого контрактные счета могут запускать транзакции только в ответ на другие транзакции, которые они получили (со счета, находящегося во внешней собственности, или с другого контрактного счета). Подробнее о звонках по контракту мы узнаем в разделе «Транзакции и сообщения».

Поэтому любое действие, которое происходит в блокчейне Ethereum, всегда приводится в движение транзакциями, запускаемыми с внешне контролируемых счетов.

Состояние счета

Состояние счета состоит из четырех компонентов, которые присутствуют независимо от типа счета:

  • nonce: Если счет является внешним счетом, этот номер представляет собой количество транзакций, отправленных с адреса счета. Если учетная запись является учетной записью контракта, nonce — это количество контрактов, созданных учетной записью.
  • баланс: Количество Вэй, принадлежащих этому адресу. Есть 1e + 18 Вэй на эфир.
  • storageRoot: хэш корневого узла дерева Меркл Патрисия (мы объясним деревья Меркла позже). Это дерево кодирует хэш содержимого хранилища этой учетной записи и по умолчанию пусто.
  • codeHash: Хэш кода EVM (Ethereum Virtual Machine — подробнее об этом позже) этой учетной записи. Для учетных записей контрактов это код, который хэшируется и сохраняется как codeHash. Для внешних учетных записей поле codeHash является хэшем пустой строки.

Мировое государство

Итак, мы знаем, что глобальное состояние Ethereum состоит из сопоставления между адресами учетных записей и состояниями учетной записи. Это сопоставление хранится в структуре данных, известной как дерево Меркла Патрисии.

Дерево Меркла (или также называемое «Меркл три») представляет собой тип двоичного дерева, состоящего из набора узлов с:

  • большое количество листовых узлов в нижней части дерева, содержащих базовые данные
  • набор промежуточных узлов, где каждый узел является хэшем двух дочерних узлов
  • один корневой узел, также образованный из хэша двух его дочерних узлов, представляющий вершину дерева

Данные в нижней части дерева генерируются путем разделения данных, которые мы хотим сохранить, на блоки, затем разделения блоков на корзины, а затем взятия хэша каждой корзины и повторения того же процесса до тех пор, пока общее количество оставшихся хэшей не станет только одним: корневой хэш.

Это дерево должно иметь ключ для каждого значения, хранящегося внутри него. Начиная с корневого узла дерева, ключ должен указывать вам, за каким дочерним узлом следовать, чтобы получить соответствующее значение, которое хранится в листовых узлах. В случае Ethereum сопоставление ключ/значение для дерева состояний находится между адресами и связанными с ними учетными записями, включая баланс, nonce, codeHash и storageRoot для каждой учетной записи (где storageRoot сам по себе является деревом).

Эта же структура используется также для хранения транзакций и квитанций. Более конкретно, каждый блок имеет «заголовок», который хранит хэш корневого узла трех различных структур Merkle trie, в том числе:

  1. Государственная тройка
  2. Транзакции
  3. Квитанции

Возможность эффективно хранить всю эту информацию в попытках Меркла невероятно полезна в Ethereum для того, что мы называем «легкими клиентами» или «легкими узлами». Помните, что блокчейн поддерживается кучей узлов. Вообще говоря, существует два типа узлов: полные узлы и легкие узлы.

Полный архивный узел синхронизирует блокчейн, загружая полную цепочку, от блока генезиса до текущего головного блока, выполняя все транзакции, содержащиеся внутри. Как правило, майнеры хранят полный узел архива, поскольку они обязаны делать это для процесса майнинга. Также можно загрузить полный узел без выполнения каждой транзакции. Несмотря на это, любой полный узел содержит всю цепочку.

Но если узлу не нужно выполнять каждую транзакцию или легко запрашивать исторические данные, на самом деле нет необходимости хранить всю цепочку. Именно здесь появляется концепция легкого узла. Вместо того, чтобы загружать и хранить полную цепочку и выполнять все транзакции, легкие узлы загружают только цепочку заголовков, от блока генезиса до текущего заголовка, без выполнения каких-либо транзакций или извлечения какого-либо связанного состояния. Поскольку легкие узлы имеют доступ к заголовкам блоков, которые содержат хэши трех попыток, они все еще могут легко генерировать и получать проверяемые ответы о транзакциях, событиях, балансах и т. Д.

Причина, по которой это работает, заключается в том, что хэши в дереве Меркла распространяются вверх — если злоумышленник попытается поменять поддельную транзакцию в нижнюю часть дерева Меркла, это изменение вызовет изменение хэша узла выше, что изменит хэш узла выше этого и так далее, до тех пор, пока он в конечном итоге не изменит корень дерева.

Любой узел, который хочет проверить часть данных, может использовать для этого то, что называется «доказательством Меркла». Доказательство Меркла состоит из:

  1. Фрагмент данных, подлежащих проверке, и их хэш
  2. Корневой хэш дерева
  3. «Ветвь» (все хэши партнеров поднимаются вверх по пути от куска к корню)

Любой, кто читает доказательство, может убедиться, что хэширование для этой ветви является последовательным на всем пути вверх по дереву, и, следовательно, что данный кусок на самом деле находится в этом положении в дереве.

Таким образом, преимущество использования дерева Merkle Patricia заключается в том, что корневой узел этой структуры криптографически зависит от данных, хранящихся в дереве, и поэтому хэш корневого узла может использоваться в качестве безопасного удостоверения для этих данных. Поскольку заголовок блока включает корневой хэш дерева состояния, транзакций и квитанций, любой узел может проверить небольшую часть состояния Ethereum без необходимости хранить все состояние, которое может быть потенциально неограниченным по размеру.

Gas и оплата

Одной из очень важных концепций в Ethereum является концепция сборов. Каждое вычисление, которое происходит в результате транзакции в сети Ethereum, влечет за собой комиссию — бесплатного обеда нет! Этот сбор уплачивается в номинале под названием «газ».

Газ — это единица, используемая для измерения сборов, необходимых для конкретных вычислений. Цена газа — это количество эфира, которое вы готовы потратить на каждую единицу газа, и измеряется в «gwei». «Вэй» — наименьшая единица эфира, где 1⁰¹⁸ Вэй представляет 1 эфир. Один гвей равен 1 000 000 000 вэй.

При каждой транзакции отправитель устанавливает лимит газа и цену на газ. Произведение цены на газ и лимита газа представляет собой максимальную сумму Вэй, которую отправитель готов заплатить за выполнение транзакции.

Например, предположим, что отправитель устанавливает лимит газа на 50 000, а цену на газ на 20 гвей. Это означает, что отправитель готов потратить не более 50 000 x 20 gwei = 1 000 000 000 000 000 000 Wei = 0,001 Ether на выполнение этой транзакции.

Помните, что лимит газа представляет собой максимальный газ, на который отправитель готов потратить деньги. Если у них достаточно эфира на балансе счета, чтобы покрыть этот максимум, они готовы к работе. Отправителю возвращается любой неиспользованный газ в конце транзакции, обмененный по первоначальному курсу.

В случае, если отправитель не предоставляет необходимый газ для выполнения транзакции, сделка заканчивается «без газа» и считается недействительной. В этом случае обработка транзакций прерывается, и любые изменения состояния, которые произошли, отменяются, так что мы в конечном итоге возвращаемся в состояние Ethereum до транзакции. Кроме того, записывается запись о сбое транзакции, показывающая, какая транзакция была предпринята и где она потерпела неудачу. А поскольку машина уже потратила усилия на выполнение расчетов до того, как закончится газ, по логике, ни один газ не возвращается отправителю.

Куда именно идут эти газовые деньги? Все деньги, потраченные отправителем на газ, отправляются на адрес «получателя», который обычно является адресом майнера. Поскольку майнеры тратят усилия на выполнение вычислений и проверку транзакций, майнеры получают плату за газ в качестве вознаграждения.

Как правило, чем выше цена на газ, которую отправитель готов заплатить, тем большую ценность майнер получает от транзакции. Таким образом, тем больше вероятность того, что майнеры выберут его. Таким образом, майнеры могут свободно выбирать, какие транзакции они хотят проверить или игнорировать. Чтобы ориентировать отправителей на то, какую цену на газ устанавливать, у майнеров есть возможность рекламировать минимальную цену на газ, по которой они будут выполнять транзакции.

Есть плата за хранение, тоже есть плата

Мало того, что газ используется для оплаты вычислительных шагов, он также используется для оплаты использования хранилища. Общая плата за хранилище пропорциональна наименьшему кратному использованию в 32 байта.

Плата за хранение имеет некоторые нюансы. Например, поскольку увеличенное хранилище увеличивает размер базы данных состояния Ethereum на всех узлах, есть стимул сохранять объем хранимых данных небольшим. По этой причине, если транзакция имеет шаг, который очищает запись в хранилище, плата за выполнение этой операции отменяется, и возмещение предоставляется за освобождение места для хранения.

Какова цель сборов?

Одним из важных аспектов работы Ethereum является то, что каждая операция, выполняемая сетью, одновременно выполняется каждым полным узлом. Однако вычислительные шаги на виртуальной машине Ethereum очень дороги. Поэтому смарт-контракты Ethereum лучше всего использовать для простых задач, таких как выполнение простой бизнес-логики или проверка подписей и других криптографических объектов, а не для более сложных применений, таких как хранение файлов, электронная почта или машинное обучение, которые могут создать нагрузку на сеть. Введение платы не позволяет пользователям перегружать сеть.

Ethereum — это полный язык Тьюринга. (Короче говоря, машина Тьюринга — это машина, которая может имитировать любой компьютерный алгоритм (для тех, кто не знаком с машинами Тьюринга, проверьте это и это). Это допускает циклы и делает Ethereum восприимчивым к проблеме остановки, проблеме, в которой вы не можете определить, будет ли программа работать бесконечно. Если бы не было никаких сборов, злоумышленник мог бы легко попытаться нарушить работу сети, выполнив бесконечный цикл внутри транзакции без каких-либо последствий. Таким образом, сборы защищают сеть от преднамеренных атак.

Вы можете подумать: «Почему мы также должны платить за хранение?» Ну, как и вычисления, хранение в сети Ethereum — это стоимость, которую вся сеть должна взять на себя.

Транзакции и сообщения

Ранее мы отмечали, что Ethereum — это конечный автомат на основе транзакций. Другими словами, транзакции, происходящие между различными учетными записями, — это то, что перемещает глобальное состояние Ethereum из одного состояния в другое.

В самом базовом смысле транзакция — это криптографически подписанная часть инструкции, которая генерируется внешней учетной записью, сериализуется, а затем отправляется в блокчейн.

Существует два типа транзакций: вызовы сообщений и создание контрактов (то есть транзакции, которые создают новые контракты Ethereum).

Все транзакции содержат следующие компоненты, независимо от их типа:

  • nonce: подсчет количества транзакций, отправленных отправителем.
  • gasPrice: количество Вэй, которое отправитель готов заплатить за единицу газа, необходимую для выполнения транзакции.
  • gasLimit: максимальное количество газа, которое отправитель готов заплатить за выполнение данной транзакции. Эта сумма устанавливается и выплачивается авансом, прежде чем будут выполнены какие-либо вычисления.
  • кому: адрес получателя. В транзакции, создающей контракт, адрес учетной записи контракта еще не существует, и поэтому используется пустое значение.
  • value: сумма Вэй, подлежащая переводу от отправителя получателю. В транзакции, создающей контракт, эта величина служит начальным балансом во вновь созданном контрактном счете.
  • v, r, s: используется для создания подписи, идентифицирующей отправителя транзакции.
  • init (существует только для транзакций, создающих контракты): фрагмент кода EVM, который используется для инициализации новой учетной записи контракта. init запускается только один раз, а затем отбрасывается. При первом запуске init возвращает тело кода учетной записи, которое является частью кода, постоянно связанного с учетной записью контракта.
  • data (необязательное поле, которое существует только для вызовов сообщений): входные данные (т.е. параметры) вызова сообщения. Например, если смарт-контракт служит службой регистрации домена, вызов этого контракта может ожидать полей ввода, таких как домен и IP-адрес.

В разделе «Учетные записи» мы узнали, что транзакции — как вызовы сообщений, так и транзакции, создающие контракты, — всегда инициируются внешними учетными записями и отправляются в блокчейн. Другой способ думать об этом заключается в том, что транзакции — это то, что соединяет внешний мир с внутренним состоянием Ethereum.

Но это не значит, что контракты не могут говорить с другими контрактами. Контракты, которые существуют в глобальной сфере действия состояния Ethereum, могут взаимодействовать с другими контрактами в той же области. Они делают это через «сообщения» или «внутренние транзакции» с другими контрактами. Мы можем думать о сообщениях или внутренних транзакциях как о похожих на транзакции, с тем основным отличием, что они НЕ генерируются внешними учетными записями. Вместо этого они генерируются контрактами. Это виртуальные объекты, которые, в отличие от транзакций, не сериализуются и существуют только в среде выполнения Ethereum.

Когда один контракт отправляет внутреннюю транзакцию другому контракту, выполняется связанный код, существующий в учетной записи контракта получателя.

Важно отметить, что внутренние транзакции или сообщения не содержат gasLimit. Это связано с тем, что лимит газа определяется внешним создателем первоначальной транзакции (т.е. некоторым внешним счетом). Лимит газа, установленный внешней учетной записью, должен быть достаточно высоким для выполнения транзакции, включая любые субисполнения, которые происходят в результате этой транзакции, такие как сообщения от контракта к контракту. Если в цепочке транзакций и сообщений у определенного сообщения заканчивается газ, то выполнение этого сообщения будет возвращено вместе с любыми последующими сообщениями, вызванными выполнением. Однако родительское выполнение не нужно возвращать.

Блоки

Все транзакции сгруппированы в «блоки». Блокчейн содержит ряд таких блоков, которые сцеплены вместе.

В Ethereum блок состоит из:

  • заголовок блока
  • информация о наборе транзакций, включенных в этот блок
  • набор других заголовков блоков для оммеров текущего блока.

Оммерс объяснил

Что, черт возьми, такое «оммер»? Ommer — это блок, родитель которого равен родительскому блоку текущего блока. Давайте быстро углубимся в то, для чего используются оммеры и почему блок содержит заголовки блоков для оммеров.

Из-за того, как построен Ethereum, время блока намного ниже (~ 15 секунд), чем у других блокчейнов, таких как Биткойн (~ 10 минут). Это позволяет ускорить обработку транзакций. Тем не менее, одним из недостатков более короткого времени блока является то, что майнеры находят более конкурирующие блочные решения. Эти конкурирующие блоки также называются «осиротевшими блоками» (т.е. добытые блоки не попадают в основную цепочку).

Цель ommers — помочь вознаграждать майнеров за включение этих осиротевших блоков. Оммеры, которые включают майнеры, должны быть «действительными», то есть в пределах шестого поколения или меньшего размера настоящего блока. После шести детей устаревшие осиротевшие блоки больше не могут упоминаться (потому что включение старых транзакций немного усложнит ситуацию).

Блоки Ommer получают меньшую награду, чем полный блок. Тем не менее, у майнеров все еще есть некоторый стимул включать эти осиротевшие блоки и пожинать награду.

Заголовок блока

Вернемся на мгновение к блокам. Мы упоминали ранее, что каждый блок имеет блок «заголовок», но что это такое?

Заголовок блока — это часть блока, состоящая из:

  • parentHash: хэш заголовка родительского блока (именно это делает набор блоков «цепочкой»)
  • ommersHash: хэш текущего списка оммеров блока
  • получатель: адрес счета, на который поступает комиссия за майнинг этого блока
  • stateRoot: хэш корневого узла state trie (вспомните, как мы узнали, что state trie хранится в заголовке и позволяет легким клиентам легко проверять что-либо о состоянии)
  • transactionsRoot: хэш корневого узла трии, содержащего все транзакции, перечисленные в этом блоке
  • receiptsRoot: хэш корневого узла трии, содержащий квитанции всех транзакций, перечисленных в этом блоке
  • logsBloomфильтр Блума (структура данных), состоящий из информации журнала
  • сложность: уровень сложности этого блока
  • число: количество текущего блока (блок генезиса имеет нулевое число блока; число блока увеличивается на 1 для каждого последующего блока)
  • gasLimit: текущий предел газа на блок
  • gasUsed: сумма общего объема газа, использованного транзакциями в этом блоке
  • метка времени: метка времени unix для создания этого блока
  • extraData: дополнительные данные, связанные с этим блоком
  • mixHash: хэш, который в сочетании с nonce доказывает, что этот блок выполнил достаточно вычислений
  • nonce: хэш, который в сочетании с mixHash доказывает, что этот блок выполнил достаточно вычислений

Обратите внимание, что каждый заголовок блока содержит три три структуры для:

  • state (stateRoot)
  • транзакции (transactionsRoot)
  • квитанции (квитанцииRoot)

Эти трие-структуры — не что иное, как попытки Меркл Патрисии, о которых мы говорили ранее.

Кроме того, есть несколько терминов из приведенного выше описания, которые стоит уточнить. Давайте посмотрим.

Журналы

Ethereum позволяет использовать журналы, позволяющие отслеживать различные транзакции и сообщения. Контракт может явно создавать журнал, определяя «события», которые он хочет регистрировать.

Запись в журнале содержит:

  • адрес учетной записи регистратора,
  • ряд тем, которые представляют различные события, осуществляемые этой транзакцией, и
  • любые данные, связанные с этими событиями.

Журналы хранятся в фильтре Bloom, который эффективно хранит бесконечные данные журнала.

Квитанция о транзакции

Журналы, хранящиеся в заголовке, взяты из сведений журнала, содержащихся в квитанции транзакции. Так же, как вы получаете квитанцию, когда покупаете что-то в магазине, Ethereum генерирует квитанцию для каждой транзакции. Как и следовало ожидать, каждая квитанция содержит определенную информацию о транзакции. Эта квитанция включает в себя такие предметы, как:

  • номер блока
  • хэш блока
  • хэш транзакции
  • газ, использованный в текущей сделке
  • кумулятивный газ, используемый в текущем блоке после выполнения текущей транзакции
  • журналы, созданные при выполнении текущей транзакции
  • .. и так далее

Сложность блока

«Сложность» блока используется для обеспечения согласованности во времени, необходимом для проверки блоков. Блок генезиса имеет сложность 131 072, и специальная формула используется для вычисления сложности каждого блока после этого. Если определенный блок проверяется быстрее, чем предыдущий блок, протокол Ethereum увеличивает сложность этого блока.

Сложность блока влияет на nonce, который представляет собой хэш, который необходимо вычислять при майнинге блока, используя алгоритм proof-of-work.

Связь между сложностью блока и nonce математически формализована как:

где Hd — сложность.

Единственный способ найти nonce, который соответствует порогу сложности, — это использовать алгоритм доказательства работы для перечисления всех возможностей. Ожидаемое время для поиска решения пропорционально сложности — чем выше сложность, тем труднее становится найти nonce, и поэтому тем сложнее проверить блок, что, в свою очередь, увеличивает время, необходимое для проверки нового блока. Таким образом, регулируя сложность блока, протокол может настроить время, необходимое для проверки блока.

Если, с другой стороны, время проверки становится медленнее, протокол уменьшает сложность. Таким образом, время проверки самонастраивается для поддержания постоянной скорости — в среднем, один блок каждые 15 секунд.

Исполнение транзакций

Мы подошли к одной из самых сложных частей протокола Ethereum: выполнению транзакции. Скажем, вы отправляете транзакцию в сеть Ethereum для обработки. Что происходит с переходом состояния Ethereum в вашу транзакцию?

Во-первых, все транзакции должны соответствовать первоначальному набору требований, чтобы быть выполненными. К ним относятся:

  • Транзакция должна быть правильно отформатирована RLP. «RLP» расшифровывается как «Префикс рекурсивной длины» и является форматом данных, используемым для кодирования вложенных массивов двоичных данных. RLP — это формат, который Ethereum использует для сериализации объектов.
  • Действительная подпись транзакции.
  • Действительная транзакция nonce. Напомним, что нонче счета — это количество транзакций, отправленных с этого счета. Чтобы быть действительной, транзакция nonce должна быть равна nonce учетной записи отправителя.
  • Лимит газа сделки должен быть равен или больше, чем внутренний газ, используемый транзакцией. Собственный газ включает в себя:
  1. заранее определенная стоимость 21 000 газа для выполнения транзакции
  2. плата за газ за данные, отправленные вместе с транзакцией (4 газа на каждый байт данных или код, равный нулю, и 68 газов на каждый ненулевой байт данных или кода)
  3. если сделка является сделкой, создающей контракт, дополнительно 32 000 газа
  • На балансе счета отправителя должно быть достаточно эфира, чтобы покрыть «авансовые» расходы на газ, которые должен оплатить отправитель. Расчет первоначальной стоимости газа прост: во-первых, лимит газа сделки умножается на цену газа сделки, чтобы определить максимальную стоимость газа. Затем эта максимальная стоимость добавляется к общей стоимости, передаваемой от отправителя получателю.

Если сделка соответствует всем вышеперечисленным требованиям к валидности, то переходим к следующему шагу.

Во-первых, мы вычитаем первоначальную стоимость исполнения из баланса отправителя и увеличиваем стоимость счета отправителя на 1 для учета текущей транзакции. На этом этапе мы можем рассчитать оставшийся газ как общий лимит газа для транзакции за вычетом используемого внутреннего газа.

Далее начинается выполнение транзакции. На протяжении всего выполнения транзакции Ethereum отслеживает «субсостояние». Это подсостояние представляет собой способ записи информации, накопленной во время транзакции, которая потребуется сразу после ее завершения. В частности, он содержит:

  • Набор самоуничтожения: набор счетов (если таковые имеются), которые будут отброшены после завершения транзакции.
  • Серия журналов: архивированные и индексируемые контрольные точки выполнения кода виртуальной машины.
  • Остаток возврата: сумма, подлежащая возврату на счет отправителя после транзакции. Помните, как мы упоминали, что хранение в Ethereum стоит денег, и что отправителю возвращают деньги за очистку хранилища? Ethereum отслеживает это с помощью счетчика возврата. Счетчик возврата начинается с нуля и увеличивается каждый раз, когда контракт удаляет что-то в хранилище.

Затем обрабатываются различные вычисления, необходимые для транзакции.

После того, как все шаги, требуемые транзакцией, были обработаны, и при условии, что нет недействительного состояния, состояние окончательно определяется путем определения количества неиспользованного газа, подлежащего возврату отправителю. В дополнение к неиспользованному газу, отправителю также возвращается некоторая сумма из «остатка возврата», который мы описали выше.

После возврата средств отправителю:

  • Эфир для газа отдается шахтеру
  • газ, используемый транзакцией, добавляется в счетчик газа блока (который отслеживает общий объем газа, используемого всеми транзакциями в блоке, и полезен при проверке блока)
  • все учетные записи в наборе самоуничтожения (если таковые имеются) удаляются

Наконец, мы остаемся с новым состоянием и набором журналов, созданных транзакцией.

Теперь, когда мы рассмотрели основы выполнения транзакций, давайте рассмотрим некоторые различия между транзакциями, создающими контракты, и вызовами сообщений.

Создание контракта

Напомним, что в Ethereum существует два типа учетных записей: контрактные счета и счета, находящиеся во внешней собственности. Когда мы говорим, что транзакция является «созданием контракта», мы имеем в виду, что целью транзакции является создание новой учетной записи контракта.

Для того, чтобы создать новую контрактную учетную запись, мы сначала объявляем адрес новой учетной записи с помощью специальной формулы. Затем мы инициализируем новую учетную запись следующим образом:

  • Установка значения nonce равным нулю
  • Если отправитель отправил некоторое количество эфира в качестве стоимости с транзакцией, установите баланс счета на это значение.
  • Вычет добавленной стоимости к балансу этого нового счета из баланса отправителя
  • Установка пустого хранилища
  • Установка кода контрактаHash в качестве хэша пустой строки

После инициализации учетной записи мы можем фактически создать учетную запись, используя init-код, отправленный вместе с транзакцией (см. раздел «Транзакция и сообщения» для обновления кода инициализации). То, что происходит во время выполнения этого кода init, варьируется. В зависимости от конструктора контракта он может обновлять хранилище учетной записи, создавать другие учетные записи контракта, совершать другие вызовы сообщений и т. Д.

Когда код для инициализации контракта выполняется, он использует газ. Сделка не позволяет использовать больше газа, чем оставшийся газ. Если это произойдет, выполнение попадет в исключение без газа (OOG) и выйдет. Если транзакция завершается из-за исключения без газа, то состояние возвращается к точке непосредственно перед транзакцией. Отправителю не возвращается газ, который был потрачен до того, как закончился.

Бу-ху.

Однако, если отправитель отправил какое-либо значение Ether вместе с транзакцией, стоимость Ether будет возвращена, даже если создание контракта не удастся. Фу!

Если код инициализации выполняется успешно, оплачивается окончательная стоимость создания контракта. Это стоимость хранения, и она пропорциональна размеру созданного кода контракта (опять же, без бесплатного обеда!) Если не остается достаточно газа для оплаты этой окончательной стоимости, то сделка снова объявляет исключение из газа и прерывается.

Если все пойдет хорошо, и мы зайдем так далеко без исключений, то любой оставшийся неиспользованный газ возвращается первоначальному отправителю транзакции, и измененное состояние теперь может сохраняться!

Ура!

Вызовы сообщений

Выполнение вызова сообщения аналогично выполнению создания контракта с некоторыми отличиями.

Выполнение вызова сообщения не включает в себя код инициализации, так как новые учетные записи не создаются. Однако он может содержать входные данные, если эти данные были предоставлены отправителем транзакции. После выполнения вызовы сообщений также имеют дополнительный компонент, содержащий выходные данные, который используется, если последующее выполнение требует этих данных.

Как и в случае с созданием контракта, если выполнение вызова сообщения завершается из-за того, что в нем заканчивается газ или потому, что транзакция недействительна (например, переполнение стека, недопустимое назначение перехода или недействительная инструкция), ни один из использованных газов не возвращается исходному вызывающему абоненту. Вместо этого весь оставшийся неиспользованный газ расходуется, и состояние сбрасывается в точку непосредственно перед передачей баланса.

До последнего обновления Ethereum не было никакого способа остановить или отменить выполнение транзакции без того, чтобы система потребляла весь газ, который вы предоставили. Например, предположим, что вы создали контракт, который выдал ошибку, когда вызывающий абонент не был авторизован для выполнения какой-либо транзакции. В предыдущих версиях Ethereum оставшийся газ по-прежнему будет потребляться, и никакой газ не будет возвращен отправителю. Но обновление Byzantium включает в себя новый код «revert», который позволяет контракту останавливать выполнение и отменять изменения состояния, не потребляя оставшийся газ, и с возможностью возврата причины неудачной транзакции. Если транзакция завершается из-за возврата, то неиспользованный газ возвращается отправителю.

Модель исполнения

До сих пор мы узнали о серии шагов, которые должны произойти, чтобы транзакция выполнялась от начала до конца. Теперь мы рассмотрим, как транзакция фактически выполняется в виртуальной машине.

Частью протокола, которая фактически обрабатывает транзакции, является собственная виртуальная машина Ethereum, известная как виртуальная машина Ethereum (EVM).

EVM — это полноценная виртуальная машина Тьюринга, как определено ранее. Единственное ограничение EVM, которого нет в типичной полной машине Тьюринга, заключается в том, что EVM неразрывно связан газом. Таким образом, общий объем вычислений, который может быть выполнен, по своей сути ограничен количеством предоставленного газа.

Кроме того, EVM имеет стековую архитектуру. Стековый компьютер — это компьютер, который использует стек «последний вход, первый выход» для хранения временных значений.

Размер каждого элемента стека в EVM составляет 256 бит, а максимальный размер стека составляет 1024.

EVM имеет память, где элементы хранятся в виде массивов байтов с адресом слова. Память изменчива, то есть она не постоянна.

EVM также имеет хранилище. В отличие от памяти, хранилище является энергонезависимым и поддерживается как часть состояния системы. EVM хранит программный код отдельно, в виртуальном ПЗУ, доступ к которому можно получить только с помощью специальных инструкций. Этим EVM отличается от типичной архитектуры фон Неймана, в которой программный код хранится в памяти или хранилище.

EVM также имеет свой собственный язык: «байт-код EVM». Когда программист, такой как вы или я, пишет смарт-контракты, которые работают на Ethereum, мы обычно пишем код на языке более высокого уровня, таком как Solidity. Затем мы можем скомпилировать это в байт-код EVM, который EVM может понять.

Хорошо, теперь перейдем к исполнению.

Перед выполнением определенного вычисления процессор проверяет доступность и достоверность следующей информации:

  • Состояние системы
  • Оставшийся газ для расчета
  • Адрес учетной записи, которой принадлежит выполняемый код
  • Адрес отправителя транзакции, вызвавшей это выполнение
  • Адрес учетной записи, вызвавшей выполнение кода (может отличаться от исходного отправителя)
  • Цена газа сделки, повлекшей за собой данное исполнение
  • Входные данные для этого выполнения
  • Значение (в Wei), переданное на этот счет в рамках текущего исполнения
  • Машинный код, подлежащий выполнению
  • Заголовок блока текущего блока
  • Глубина стека вызова или создания контракта в настоящем сообщении

В начале выполнения память и стек пусты, а счетчик программы равен нулю.

PC: 0 STACK: [] MEM: [], STORAGE: {}

Затем EVM выполняет транзакцию рекурсивно, вычисляя состояние системы и состояние машины для каждого цикла. Состояние системы — это просто глобальное состояние Ethereum. Состояние компьютера состоит из:

  • газ в наличии
  • счетчик программ
  • содержимое памяти
  • активное количество слов в памяти
  • содержимое стека.

Элементы стека добавляются или удаляются из крайней левой части серии.

В каждом цикле соответствующее количество газа уменьшается из оставшегося газа, а программа счетчиков увеличивается.

В конце каждого цикла есть три возможности:

  1. Машина достигает исключительного состояния (например, недостаточное количество газа, недопустимые инструкции, недостаточные элементы штабеля, элементы стека будут переполняться выше 1024, недопустимое назначение JUMP/JUMPI и т. Д.) И поэтому должна быть остановлена, а любые изменения отброшены.
  2. Последовательность продолжает процесс в следующий цикл
  3. Машина достигает контролируемой остановки (конец процесса выполнения)

Предполагая, что выполнение не достигает исключительного состояния и достигает «контролируемой» или нормальной остановки, машина генерирует результирующее состояние, оставшийся газ после этого выполнения, накопленное подсостояние и результирующий выход.

Фу. Мы прошли через одну из самых сложных частей Ethereum. Даже если вы не до конца поняли эту часть, это нормально. Вам на самом деле не нужно понимать мельчайшие детали исполнения, если вы не работаете на очень глубоком уровне.

Как завершается блокировка

Наконец, давайте посмотрим, как завершается блок из многих транзакций.

Когда мы говорим «завершено», это может означать две разные вещи, в зависимости от того, является ли блок новым или существующим. Если это новый блок, мы имеем в виду процесс, необходимый для майнинга этого блока. Если это существующий блок, то мы говорим о процессе проверки блока. В любом случае, существует четыре требования к блоку, который должен быть «доработан»:

1) Проверка (или, если майнинг, определение) Ommers Каждый блок ommer в заголовке блока должен быть допустимым
заголовком и находиться в шестом поколении настоящего блока.

2) Валидация (или, если майнинг, определение) транзакцийЧисление
использования газа на блоке должно быть равно совокупному газу, используемому транзакциями, перечисленными в блоке. (Напомним, что при выполнении транзакции мы отслеживаем счетчик газа блока, который отслеживает общий объем газа, используемого всеми транзакциями в блоке).

3) Применяйте вознаграждения (только если майнинг)
Адрес получателя начисляется 5 эфиров за майнинг блока. (Согласно предложению Ethereum EIP-649, эта награда в размере 5 ETH скоро будет уменьшена до 3 ETH). Кроме того, за каждый оммер бенефициару текущего блока присуждается дополнительная 1/32 вознаграждения за текущий блок. Наконец, бенефициар блока (блоков) ommer также получает определенную сумму (есть специальная формула для того, как это рассчитывается).

4) Проверьте (или, если майнинг, вычислите допустимое) состояние и nonceEnsure, что все транзакции
и результирующие изменения состояния применяются, а затем определите новый блок как состояние после того, как вознаграждение за блок было применено к конечному состоянию транзакции. Проверка выполняется путем проверки этого конечного состояния по трие состояния, хранящемуся в заголовке.

Осмысление работы

В разделе «Блоки» кратко рассматривается понятие сложности блока. Алгоритм, который придает смысл сложности блока, называется Proof of Work (PoW).

Алгоритм доказательства работы Ethereum называется «Ethash» (ранее известный как Dagger-Hashimoto).

Алгоритм формально определяется как:

где m — mixHashn — nonceHn — заголовок нового блока (исключая компоненты nonce и mixHash, которые должны быть вычислены), Hn — nonce заголовка блока и d — это группа обеспечения доступности баз данных, представляющая собой большой набор данных.

В разделе «Блоки» мы говорили о различных элементах, которые существуют в заголовке блока. Два из этих компонентов назывались mixHash и nonce. Как вы помните:

  • mixHash — это хэш, который в сочетании с nonce доказывает, что этот блок выполнил достаточно вычислений.
  • nonce — это хэш, который в сочетании с mixHash доказывает, что этот блок выполнил достаточно вычислений

Функция PoW используется для оценки этих двух элементов.

То, как именно вычисляются mixHash и nonce с помощью функции PoW, несколько сложно, и мы можем углубиться в отдельный пост. Но на высоком уровне это работает так:

Для каждого блока вычисляется «семя». Это семя отличается для каждой «эпохи», где каждая эпоха имеет длину 30 000 блоков. Для первой эпохи seed — это хэш ряда из 32 байт нулей. Для каждой последующей эпохи это хэш предыдущего семенного хэша. Используя это семя, узел может вычислить псевдослучайный «кэш».

Этот кэш невероятно полезен, потому что он позволяет использовать концепцию «легких узлов», которую мы обсуждали ранее в этом посте. Цель легких узлов состоит в том, чтобы предоставить определенным узлам возможность эффективно проверять транзакцию без бремени хранения всего набора данных блокчейна. Легкий узел может проверить действительность транзакции исключительно на основе этого кэша, поскольку кэш может повторно создать конкретный блок, который необходимо проверить.

Используя кэш, узел может создать «набор данных» группы обеспечения доступности баз данных, где каждый элемент в наборе данных зависит от небольшого количества псевдослучайно выбранных элементов из кэша. Чтобы быть майнером, вы должны сгенерировать этот полный набор данных; все полные клиенты и майнеры хранят этот набор данных, и набор данных растет линейно со временем.

Затем майнеры могут взять случайные фрагменты набора данных и поместить их через математическую функцию, чтобы хэшировать их вместе в «mixHash». Майнер будет многократно генерировать mixHash до тех пор, пока выходные данные не окажутся ниже желаемого целевого nonce. Когда выходные данные соответствуют этому требованию, этот nonce считается допустимым, и блок может быть добавлен в цепочку.

Майнинг как механизм
безопасности В целом, цель PoW состоит в том, чтобы доказать криптографически безопасным способом, что определенное количество вычислений было затрачено на генерацию некоторого результата (т.е. nonce). Это связано с тем, что нет лучшего способа найти nonce, который ниже требуемого порога, кроме как перечислить все возможности. Результаты многократного применения хэш-функции имеют равномерное распределение, и поэтому мы можем быть уверены, что в среднем время, необходимое для поиска такого nonce, зависит от порога сложности. Чем выше сложность, тем больше времени требуется для решения для nonce. Таким образом, алгоритм PoW придает смысл понятию сложности, которое используется для обеспечения безопасности блокчейна.

Что мы подразумеваем под безопасностью блокчейна? Все просто: мы хотим создать блокчейн, которому доверяют ВСЕ. Как мы обсуждали ранее в этом посте, если существует более одной цепочки, пользователи потеряют доверие, потому что они не смогут разумно определить, какая цепочка является «действительной» цепочкой. Для того, чтобы группа пользователей приняла базовое состояние, которое хранится в блокчейне, нам нужен единый канонический блокчейн, в который верит группа людей.

Это именно то, что делает алгоритм PoW: он гарантирует, что конкретный блокчейн останется каноническим в будущем, что делает невероятно трудным для злоумышленника создание новых блоков, которые перезаписывают определенную часть истории (например, стирая транзакции или создавая поддельные транзакции) или поддерживая форк. Чтобы сначала проверить свой блок, злоумышленник должен будет последовательно решать проблему nonce быстрее, чем кто-либо другой в сети, так что сеть считает, что их цепочка является самой тяжелой цепочкой (на основе принципов протокола GHOST, о которых мы упоминали ранее). Это было бы невозможно, если бы у злоумышленника не было более половины мощности сетевого майнинга, сценарий, известный как атака 51%.

Осмысление как механизм распределения богатства

Помимо обеспечения безопасного блокчейна, PoW также является способом распределения богатства среди тех, кто тратит свои вычисления на обеспечение этой безопасности. Напомним, что майнер получает вознаграждение за добычу блока, в том числе:

  • награда за статический блок в размере 5 эфиров за блок «выигрыша» (вскоре будет заменен на 3 эфира))
  • стоимость газа, затраченного внутри блока транзакциями, включенными в блок
  • дополнительное вознаграждение за включение ommers в состав блока

Чтобы гарантировать, что использование механизма консенсуса PoW для безопасности и распределения богатства является устойчивым в долгосрочной перспективе, Ethereum стремится привить эти два свойства:

  • Сделайте его доступным для как можно большего количества людей. Другими словами, людям не должно понадобиться специализированное или необычное оборудование для запуска алгоритма. Цель этого состоит в том, чтобы сделать модель распределения богатства максимально открытой, чтобы каждый мог предоставить любое количество вычислительной мощности в обмен на эфир.
  • Уменьшите возможность для любого отдельного узла (или небольшого набора) получить непропорционально большую прибыль. Любой узел, который может получить непропорционально большую сумму прибыли, означает, что узел имеет большое влияние на определение канонического блокчейна. Это проблематично, потому что это снижает сетевую безопасность.

В блокчейн-сети Bitcoin одна из проблем, которая возникает в связи с двумя вышеуказанными свойствами, заключается в том, что алгоритм PoW является хэш-функцией SHA256. Слабость этого типа функции заключается в том, что ее можно решить гораздо более эффективно с помощью специализированного оборудования, также известного как ASIC.

Чтобы смягчить эту проблему, Ethereum решил сделать свой алгоритм PoW (Ethhash) последовательно жестким для памяти. Это означает, что алгоритм спроектирован таким образом, что вычисление nonce требует много памяти и пропускной способности. Большие требования к памяти затрудняют параллельное использование памяти компьютером для одновременного обнаружения нескольких nonces, а высокие требования к пропускной способности затрудняют обнаружение нескольких nonce даже для сверхбыстрого компьютера одновременно. Это снижает риск централизации и создает более равные условия для узлов, выполняющих проверку.

Следует отметить, что Ethereum переходит от механизма консенсуса PoW к чему-то, называемому «доказательством доли». Это сама по себе звериная тема, которую мы надеемся исследовать в будущем посте. ☺️

Заключение

… Фу! Вы дошли до конца. Надеюсь?

Я знаю, что в этом посте есть что переварить. Если вам требуется несколько чтений, чтобы полностью понять, что происходит, это совершенно нормально. Я лично много раз читал желтую бумагу Ethereum, белую книгу и различные части кодовой базы, прежде чем рассказать о том, что происходит.

Тем не менее, я надеюсь, что вы нашли этот обзор полезным. Если вы обнаружите какие-либо ошибки или ошибки, я хотел бы, чтобы вы написали личную заметку или опубликовали ее непосредственно в комментариях. Я смотрю на них всех, обещаю 😉

И помните, я человек (да, это правда) и я делаю ошибки. Я нашел время, чтобы написать этот пост на благо сообщества, бесплатно. Поэтому, пожалуйста, будьте конструктивны в своих отзывах, без ненужных нападок.

Источник