Транзакции
Транзакции — это криптографически подписанные инструкции от аккаунтов. Аккаунт инициирует транзакцию для обновления состояния сети Эфириум. Самая простая транзакция — это перевод ETH с одного аккаунта на другой.
Предварительные требования
Чтобы лучше понять эту страницу, мы рекомендуем сначала прочитать про аккаунты и наше введение в Эфириум.
Что такое транзакция?
Транзакция Эфириума — это действие, инициированное внешне принадлежащим аккаунтом (externally-owned account, EOA), то есть аккаунтом, управляемым человеком, а не контрактом. Например, если Боб отправляет Алисе 1 ETH, счет Боба должен быть списан, а счет Алисы — пополнен. Это действие, изменяющее состояние, происходит в рамках транзакции.
Схема адаптирована из Ethereum EVM illustrated (opens in a new tab)
Транзакции, которые изменяют состояние EVM, должны транслироваться всей сети. Любой узел может транслировать запрос на выполнение транзакции в EVM; после этого валидатор выполнит транзакцию и распространит полученное изменение состояния на остальную часть сети.
Транзакции требуют комиссии и должны быть включены в проверенный блок. Чтобы упростить этот обзор, мы рассмотрим комиссии за газ и валидацию в другом месте.
Отправленная транзакция включает следующую информацию:
from— адрес отправителя, который будет подписывать транзакцию. Это будет внешне принадлежащий аккаунт, так как контрактные аккаунты не могут отправлять транзакцииto— адрес получателя (если это внешне принадлежащий аккаунт, транзакция переведет средства. Если это контрактный аккаунт, транзакция выполнит код контракта)signature— идентификатор отправителя. Он генерируется, когда приватный ключ отправителя подписывает транзакцию и подтверждает, что отправитель авторизовал эту транзакциюnonce— последовательно увеличивающийся счетчик, который указывает номер транзакции с аккаунта (нонс)value— сумма ETH для перевода от отправителя к получателю (выраженная в Wei, где 1 ETH равен 1e+18 Wei)input data— необязательное поле для включения произвольных данныхgasLimit— максимальное количество единиц газа, которое может быть потреблено транзакцией (лимит газа). EVM определяет единицы газа, необходимые для каждого вычислительного шагаmaxPriorityFeePerGas— максимальная цена потребленного газа, которая будет включена в качестве чаевых валидаторуmaxFeePerGas— максимальная комиссия за единицу газа, которую готовы заплатить за транзакцию (включаяbaseFeePerGasиmaxPriorityFeePerGas)
Газ — это ссылка на вычисления, необходимые для обработки транзакции валидатором. Пользователи должны платить комиссию за эти вычисления. gasLimit и maxPriorityFeePerGas определяют максимальную комиссию за транзакцию, выплачиваемую валидатору. Подробнее о газе.
Объект транзакции будет выглядеть примерно так:
{
from: "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
to: "0xac03bb73b6a9e108530aff4df5077c2b3d481e5a",
gasLimit: "21000",
maxFeePerGas: "300",
maxPriorityFeePerGas: "10",
nonce: "0",
value: "10000000000"
}
Но объект транзакции должен быть подписан с использованием приватного ключа отправителя. Это доказывает, что транзакция могла исходить только от отправителя и не была отправлена мошенническим путем.
Клиент Эфириума, такой как Go Ethereum (Geth), будет обрабатывать этот процесс подписания.
Пример вызова JSON-RPC:
{
"id": 2,
"jsonrpc": "2.0",
"method": "account_signTransaction",
"params": [
{
"from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"gas": "0x55555",
"maxFeePerGas": "0x1234",
"maxPriorityFeePerGas": "0x1234",
"input": "0xabcd",
"nonce": "0x0",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x1234"
}
]
}
Пример ответа:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"tx": {
"nonce": "0x0",
"maxFeePerGas": "0x1234",
"maxPriorityFeePerGas": "0x1234",
"gas": "0x55555",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x1234",
"input": "0xabcd",
"v": "0x26",
"r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
"s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
}
}
}
raw— это подписанная транзакция в закодированной форме Recursive Length Prefix (RLP)tx— это подписанная транзакция в формате JSON
С помощью хеша подписи можно криптографически доказать, что транзакция исходила от отправителя и была отправлена в сеть.
Поле данных
Подавляющее большинство транзакций обращаются к контракту с внешне принадлежащего аккаунта. Большинство контрактов написаны на Solidity и интерпретируют свое поле данных в соответствии с .
Первые четыре байта указывают, какую функцию вызывать, используя хеш имени функции и ее аргументов. Иногда вы можете определить функцию по селектору, используя эту базу данных (opens in a new tab).
Остальная часть данных вызова (calldata) — это аргументы, закодированные в соответствии со спецификациями ABI (opens in a new tab).
Например, давайте посмотрим на эту транзакцию (opens in a new tab). Используйте Click to see More (Нажмите, чтобы увидеть больше), чтобы просмотреть данные вызова.
Селектор функции — 0xa9059cbb. Существует несколько известных функций с этой подписью (opens in a new tab).
В данном случае исходный код контракта (opens in a new tab) был загружен на Etherscan, поэтому мы знаем, что это функция transfer(address,uint256).
Остальные данные:
0000000000000000000000004f6742badb049791cd9a37ea913f2bac38d01279
000000000000000000000000000000000000000000000000000000003b0559f4
Согласно спецификациям ABI, целочисленные значения (такие как адреса, которые являются 20-байтовыми целыми числами) отображаются в ABI как 32-байтовые слова, дополненные нулями спереди.
Поэтому мы знаем, что адрес to — это 4f6742badb049791cd9a37ea913f2bac38d01279 (opens in a new tab).
value равно 0x3b0559f4 = 990206452.
Дескрипторы транзакций
Поскольку поле данных содержит непрозрачные шестнадцатеричные байты, может быть крайне сложно проверить, какое действие на самом деле выполнит транзакция. Эта уязвимость «слепого подписания» устраняется с помощью прозрачного подписания (Clear Signing) (opens in a new tab) за счет использования дескрипторов транзакций (opens in a new tab) (определенных в ERC-7730).
Спецификация ERC-7730 использует дескрипторы транзакций (часто структурированные как файлы JSON) для обогащения данных, содержащихся в ABI и структурированных сообщениях, таких как данные вызова транзакций EVM, сообщения EIP-712 и пользовательские операции (User Operations) EIP-4337. Разработчики используют эти дескрипторы для сопоставления конкретных переменных транзакции непосредственно с шаблонами форматирования, гарантируя, что базовые данные остаются машиночитаемыми для приложений.
На фронтенде кошельки используют этот контекст форматирования для перевода непрозрачного байт-кода в понятную, удобочитаемую информацию. Автоматически преобразуя такие значения, как адреса токенов, в распознаваемые тикеры, или суммы в десятичные дроби, пользователи получают сводку точного намерения транзакции на простом языке (например, «Своп 1000 USDC на как минимум 0.25 обернутого эфира (WETH)») перед тем, как они ее подпишут.
Типы транзакций
В Эфириуме существует несколько различных типов транзакций:
- Обычные транзакции: транзакция с одного аккаунта на другой.
- Транзакции развертывания контракта: транзакция без адреса получателя ('to'), где поле данных используется для кода контракта.
- Выполнение контракта: транзакция, которая взаимодействует с развернутым смарт-контрактом. В этом случае адрес получателя ('to') — это адрес смарт-контракта.
О газе
Как уже упоминалось, выполнение транзакций требует газа. Простые транзакции перевода требуют 21000 единиц газа.
Таким образом, чтобы Боб отправил Алисе 1 ETH при baseFeePerGas в 190 Gwei и maxPriorityFeePerGas в 10 Gwei, Бобу нужно будет заплатить следующую комиссию:
(190 + 10) * 21000 = 4,200,000 gwei
--или--
0.0042 ETH
Со счета Боба будет списано -1.0042 ETH (1 ETH для Алисы + 0.0042 ETH в виде комиссии за газ)
На счет Алисы будет зачислено +1.0 ETH
Базовая комиссия будет сожжена -0.00399 ETH
Валидатор оставляет себе чаевые +0.000210 ETH
Схема адаптирована из Ethereum EVM illustrated (opens in a new tab)
Любой газ, не использованный в транзакции, возвращается на аккаунт пользователя.
Взаимодействие со смарт-контрактами
Газ требуется для любой транзакции, в которой задействован смарт-контракт.
Смарт-контракты также могут содержать функции, известные как функции view (opens in a new tab) или pure (opens in a new tab), которые не изменяют состояние контракта. Таким образом, вызов этих функций с EOA не потребует газа. Базовым вызовом RPC для этого сценария является eth_call.
В отличие от доступа с использованием eth_call, эти функции view или pure также часто вызываются внутренне (то есть из самого контракта или из другого контракта), что требует затрат газа.
Жизненный цикл транзакции
После отправки транзакции происходит следующее:
- Криптографически генерируется хеш транзакции:
0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017 - Затем транзакция транслируется в сеть и добавляется в пул транзакций, состоящий из всех остальных ожидающих транзакций сети.
- Валидатор должен выбрать вашу транзакцию и включить ее в блок, чтобы проверить транзакцию и считать ее «успешной».
- Со временем блок, содержащий вашу транзакцию, будет обновлен до статуса «обоснованный» (justified), а затем «финализированный» (finalized). Эти обновления дают гораздо большую уверенность в том, что ваша транзакция была успешной и никогда не будет изменена. Как только блок становится «финализированным», его можно изменить только с помощью атаки на уровне сети, которая будет стоить много миллиардов долларов.
Визуальная демонстрация
Посмотрите, как Остин рассказывает о транзакциях, газе и майнинге.
Типизированный конверт транзакции
Изначально в Эфириуме был один формат для транзакций. Каждая транзакция содержала нонс, цену газа, лимит газа, адрес получателя, значение, данные, v, r и s. Эти поля кодируются с помощью RLP и выглядят примерно так:
RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])
Эфириум эволюционировал для поддержки нескольких типов транзакций, чтобы позволить реализовывать новые функции, такие как списки доступа и EIP-1559 (opens in a new tab), не затрагивая устаревшие форматы транзакций.
EIP-2718 (opens in a new tab) — это то, что делает такое поведение возможным. Транзакции интерпретируются как:
TransactionType || TransactionPayload
Где поля определяются как:
TransactionType— число от 0 до 0x7f, что в сумме дает 128 возможных типов транзакций.TransactionPayload— произвольный массив байтов, определяемый типом транзакции.
На основе значения TransactionType транзакция может быть классифицирована как:
-
Транзакции типа 0 (устаревшие): Исходный формат транзакций, используемый с момента запуска Эфириума. Они не включают функции из EIP-1559 (opens in a new tab), такие как динамические вычисления комиссии за газ или списки доступа для смарт-контрактов. Устаревшие транзакции не имеют специального префикса, указывающего их тип в сериализованной форме, и начинаются с байта
0xf8при использовании кодировки Recursive Length Prefix (RLP). Значение TransactionType для этих транзакций равно0x0. -
Транзакции типа 1: Представленные в EIP-2930 (opens in a new tab) как часть обновления Берлин (Berlin) в Эфириуме, эти транзакции включают параметр
accessList. Этот список указывает адреса и ключи хранилища, к которым транзакция ожидает получить доступ, помогая потенциально снизить затраты газа для сложных транзакций с участием смарт-контрактов. Изменения рынка комиссий EIP-1559 не включены в транзакции типа 1. Транзакции типа 1 также включают параметрyParity, который может быть либо0x0, либо0x1, указывая на четность значения y подписи secp256k1. Они идентифицируются тем, что начинаются с байта0x01, а их значение TransactionType равно0x1. -
Транзакции типа 2, обычно называемые транзакциями EIP-1559, — это транзакции, представленные в EIP-1559 (opens in a new tab) в рамках обновления Лондон (London) в Эфириуме. Они стали стандартным типом транзакций в сети Эфириум. Эти транзакции вводят новый механизм рынка комиссий, который улучшает предсказуемость за счет разделения комиссии за транзакцию на базовую комиссию и приоритетную комиссию. Они начинаются с байта
0x02и включают такие поля, какmaxPriorityFeePerGasиmaxFeePerGas. Транзакции типа 2 теперь используются по умолчанию из-за их гибкости и эффективности, и особенно предпочтительны в периоды высокой перегрузки сети за их способность помогать пользователям более предсказуемо управлять комиссиями за транзакции. Значение TransactionType для этих транзакций равно0x2. -
Транзакции типа 3 (блоб-транзакции) были представлены в EIP-4844 (opens in a new tab) как часть обновления Dencun в Эфириуме. Эти транзакции предназначены для более эффективной обработки данных «блобов» (Binary Large Objects), что особенно выгодно для роллапов уровня 2 (l2), поскольку предоставляет способ публикации данных в сети Эфириум с меньшими затратами. Блоб-транзакции включают дополнительные поля, такие как
blobVersionedHashes,maxFeePerBlobGasиblobGasPrice. Они начинаются с байта0x03, а их значение TransactionType равно0x3. Блоб-транзакции представляют собой значительное улучшение доступности данных (DA) и возможностей масштабирования Эфириума. -
Транзакции типа 4 были представлены в EIP-7702 (opens in a new tab) как часть обновления Пектра (Pectra) в Эфириуме. Эти транзакции разработаны с учетом прямой совместимости с абстракцией учетной записи. Они позволяют EOA временно вести себя как контрактные аккаунты без ущерба для их исходной функциональности. Они включают параметр
authorization_list, который указывает смарт-контракт, которому EOA делегирует свои полномочия. После транзакции поле кода EOA будет содержать адрес делегированного смарт-контракта.
Дополнительная литература
Знаете ресурс сообщества, который помог вам? Отредактируйте эту страницу и добавьте его!