May 3, 2022

Эфир это моя жизнь

Всем привет! С вами Тёма!

Сегодня мы постараемся создать довольно полную и понятную методичку по эфиру, чтобы все необходимые знания находилось в одном месте!

Начнем!

Что по основам?

Начну с того, что тут должен был быть большой блок с подробным описанием работы Ethash, но он получился слишком хардовым, поэтому если хотите сами все разобрать, то ТЫК сюда!

Но мы все же чуть-чуть затронем эту тему!

Почему же Ethash, а не SHA-256?

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

Теперь мы перейдем к нодам!

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

Теперь поговорим о майнерах!

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

А что такое транзакция?

Если коротко, то транзакция это изменение состояния системы, которое относится к действию, инициированному внешней учетной записью(человеком, а не контрактом). Транзакция включает в себя следующую информацию:

  • Получатель — адрес получателя (если внешняя учетная запись, то транзакция будет передавать значение. Если контрактная учетная запись, то транзакция выполнит код контракта)
  • Подпись – идентификатор отправителя. Он генерируется, когда закрытый ключ отправителя подписывает транзакцию и подтверждает, что отправитель авторизовал эту транзакцию
  • Стоимость — количество ETH для перевода от отправителя к получателю
  • Данные — необязательное поле для включения произвольных данных
  • gasLimit — максимальное количество единиц газа, которое может быть израсходовано транзакцией
  • maxPriorityFee - максимальное количество газа, которое будет включено в качестве чаевых майнеру
  • maxFee - максимальное количество газа, готовое к оплате за транзакцию (включая baseFeePerGas и maxPriorityFeePerGas)
  • nonce - порядковый номер транзакции со счета-отправителя

Примерно так выглядит обычная транзакция в коде:

{  
   from: "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
   to: "0xac03bb73b6a9e108530aff4df5077c2b3d481e5a",
   gasLimit: "21000",
   maxFeePerGas: "300",
   maxPriorityFeePerGas: "10",
   nonce: "0",
   value: "10000000000"
}

А как же нам с контрактами взаимодействовать?

Подавляющее большинство транзакций получают доступ к контракту из внешней учетной записи. Большинство контрактов написаны на Solidity и интерпретируют свои поля данных в соответствии с ABI

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

Вот элементарный пример данных переданных при транзакции на контракт. Тут мы видим в первой строке четыре байта выбора функции (0х не считается), а после две строке со входными на эту функцию данными

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

Типы транзакций

  • Обычные транзакции: транзакция из одного кошелька в другой
  • Транзакции деплоя контракта: транзакция без адреса «кому», где поле данных используется для кода контракта
  • Исполнение контракта: транзакция, которая взаимодействует со смарт-контрактом. В этом случае адрес «кому» — это адрес смарт-контракта

Жизненный цикл транзакции

  • После отправки транзакции криптография генерирует хэш транзакции, например такой: 0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017
  • Затем транзакция транслируется в сеть и включается в мемпул (пул с множеством других транзакций, типо отстойника, именно в этот момент ваша транзакция находится в ожидании (pending))
  • Теперь майнер должен выбрать вашу транзакцию и включить ее в блок, чтобы проверить транзакцию и считать ее «успешной». Приоритет майнеры отдают тем, кто ставил больший maxPriorityFee. А еще вы можете ждать на этом этапе некоторое время, если сеть занята, а майнеры не в состоянии идти в ногу со временем, ну или если вы газ неправильно выставили)
  • Ваша транзакция получает «подтверждение». Количество подтверждений — это количество блоков, созданных после блока, в который была включена ваша транзакция. Чем выше число, тем больше уверенность в том, что сеть обработала и распознала транзакцию грамотно

Блок

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

Примерно так выглядит типичный блок

{
  difficulty: 134290,
  extraData: "0xd783010506846765746887676f312e372e33856c696e7578",
  gasLimit: 4038569465,
  gasUsed: 23114,
  hash: "0xa5d727b111f123e11b6dc5d271697b82a6238f83ab342088e0a1538afce7862d",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000004000000000000200000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x4c558858289c4180d0d2b994a4e009e078731191",
  mixHash: "0xba14e2b605aeacfa68f8345a9e30dd2d37dc4f3f4eba9e8ac8e744ded90ae566",
  nonce: "0x38de415ea086671a",
  number: 63,
  parentHash: "0xa7186a94afe92f0c0fedd1b8aa96e9aa92321e63a0b79d3f68ffe888bfb0239a",
  receiptsRoot: "0x303d0444bf28744722fd53a46144ead61f3515a82b9640603028ebf91f212126",
  sha3Uncles: "0x3633e886ef643c067d6fb7bfc1d2a6bf3eba939bf3cbb522f3894779ac1dd090",
  size: 1709,
  stateRoot: "0xcd4abaafec19113743df0235f06482f3a0b49e546e708055aa7a2382b232601e",
  timestamp: 1484750319,
  totalDifficulty: 8362288,
  transactions: ["0x1bd5825eac201f0f0e1e2c4a9ed5de026edc3f7fb02c4b912ca55c0f50a021fc"],
  transactionsRoot: "0xbaf6819c91ed625a97e974bbc4efd5808970b3b4991b1e2aca0dd537750aafc7",
  uncles: ["0x6f0a9ab0e468112fdcbbecd81ebebf929ca9a38e6a4c864e8164309d3bed42c6", "0xb4dd60c91ab18de3f72ba27ab5934bec66ab01798d18b599d1d298bd29e56204"]
}

Сейчас мы быстренько пробежимся по каждой из состовляющих

  • parentHash: 256-битный хэш Keccak заголовка родительского блока
  • ommersHash: 256-битный хэш Keccak части списка оммеров этого блока
  • beneficiary: 160-битный адрес, на который будут переведены все сборы, полученные от успешного майнинга этого блока
  • stateRoot: 256-битный хэш Keccak корневого узла дерева Мирового состояния после выполнения всех транзакций и финализации (Мировое состояние — сопоставление между адресами и состояниями учетных записей. Его можно рассматривать как глобальное состояние, которое постоянно обновляется при выполнении транзакций. Сеть Ethereum представляет собой децентрализованный компьютер, а состояние дерева считается жестким диском. Вся информация об учетных записях хранится в дереве состояния мира, и вы можете получить информацию, запросив ее. Мировое дерево состояний тесно связано с деревом хранилища учетной записи, поскольку оно имеет поле «storageRoot», указывающее на корневой узел в дереве хранилища учетной записи (источник))
  • transactionRoot: 256-битный хэш Keccak корневого узла trie структуры, заполняемой каждой транзакцией в части списка транзакций блока (Transaction trie записывает транзакции в Ethereum. Транзакция играет ключевую роль в изменении состояний, поскольку Ethereum — это «машина состояний», основанная на транзакциях. Как только транзакция записывается в блок, ее нельзя изменить, чтобы подтвердить баланс счетов (состояние мира). Поскольку Transaction Trie создается с помощью Modified Merkel Patricia Trie, единственный корневой узел хранится в блоке (источник))
  • receiptsRoot: 256-битный хэш Keccak корневого узла trie структуры, заполненный квитанциями каждой транзакции в части списка транзакций блока (Transaction Receipt Trie записывает квитанции транзакций. Квитанция является результатом транзакции, которая успешно выполнена. Квитанция включает в себя хэш транзакции, номер блока, количество использованного газа, адрес контракта и т. д. (источник))
  • logsBloom: фильтр Блума, состоящий из индексируемой информации (адресов и логов), содержащейся в каждой записи логов с момента получения каждой транзакции в списке транзакций (События в системе ethereum должны быть легко доступны для поиска, чтобы приложения могли фильтровать и отображать события, в том числе исторические, без чрезмерных затрат. В то же время место для хранения стоит дорого, поэтому мы не хотим хранить много дублирующихся данных, таких как список транзакций и логи, которые они генерируют. Для решения этой проблемы существует logs bloom filter. Когда блок создается или проверяется, адрес любого контракта веденного в лог и все проиндексированные поля из логов, созданных в результате выполнения этих транзакций, добавляются в фильтр Блума, который включается в заголовок блока. Фактические логи не включены в данные блока для экономии места Когда приложение хочет найти все записи логов из данного контракта или с определенными проиндексированными полями (или и то, и другое), узел может быстро просмотреть заголовок каждого блока, проверяя фильтр Блума, чтобы увидеть, могут ли они содержать соответствующие логи. Если это так, узел повторно выполняет транзакции из этого блока, регенерируя логи и возвращая соответствующие в приложение)
  • difficulty: скалярное значение, соответствующее уровню сложности этого блока. Это можно рассчитать на основе уровня сложности предыдущего блока и timestamp
  • number: скалярное значение, равное количеству блоков-предков. Генезисный блок имеет нулевой номер
  • gasLimit: скалярное значение, равное текущему лимиту расхода газа на блок
  • gasUsed: скалярное значение, равное общему количеству газа, использованного в транзакциях в этом блоке
  • timestamp: время, когда майнер добыл блок
  • extraData: Произвольный массив байтов, содержащий данные, относящиеся к этому блоку. Это должно быть 32 байта или меньше
  • mixHash: 256-битный хеш, который в сочетании с nonce доказывает, что в этом блоке было выполнено достаточное количество вычислений
  • nonce: 64-битное значение, которое в сочетании с mixHash доказывает, что в этом блоке было выполнено достаточное количество вычислений (его и подбирают майнеры)

Вот еще дополнительный график о системе формировании блоков

Что влияет на размер блока?

Сами блоки ограничены по размеру. Каждый блок имеет целевой размер 15 миллионов газа, но размер блоков будет увеличиваться или уменьшаться в соответствии с потребностями сети, вплоть до предела блока в 30 миллионов газа. Общее количество газа, расходуемого всеми транзакциями в блоке, должно быть меньше лимита газа блока. Это важно, потому что гарантирует, что блоки не могут быть произвольно большими. Если бы блоки могли быть произвольно большими, то менее производительные полные узлы постепенно перестали бы идти в ногу с сетью из-за требований к пространству и скорости

Газ

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

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

Надеюсь статья была интересной и понятной!

Мой телеграмм канал - https://t.me/ortomich_crypto