Объяснение EIP-3074
Краткий обзор:
Абстракция аккаунтов остаётся одной из главных проблем для разработчиков Ethereum. Недавно одобренный EIP-3074 направлен на то, чтобы сделать первый шаг к сокращению разрыва между виртуальной машиной Ethereum (EVM) и пользовательским опытом традиционных финансовых систем.
С введением двух новых опкодов — AUTH и AUTHCALL — EIP-3074 предоставляет внешне управляемым аккаунтам (EOA) функционал аккаунтов на базе смарт-контрактов (SCA) и изменяет способ взаимодействия пользователей с EVM.
EIP-3074 нацелен на «ускорение» работы EOA!
Авторизованные транзакции открывают для пользователей EOA следующие возможности:
- Спонсируемые транзакции
- Выполнение нескольких действий в одной транзакции
- Путь к восстановлению активов в случае утраты приватного ключа
Однако, несмотря на все преимущества, EIP-3074 имеет и некоторые потенциальные недостатки:
- Проблемы с безопасностью. Злоумышленник может украсть средства пользователей.
- Возможные проблемы с обновлениями Ethereum в будущем
Текущие недостатки EOAs
EOA по сути представляет собой просто сохранённый приватный ключ, который кодирует публичный ключ и адрес блокчейна пользователя. Несмотря на то, что EOAs соответствуют основному принципу самострахования активов на блокчейне, они не обеспечивают тот пользовательский опыт, к которому привыкли пользователи традиционных кошельков.
Ограничения современных EOAs можно резюмировать следующим образом:
- Каждое действие в сети требует отдельной транзакции (особенно если требуется апрув токенов).
- Для выполнения всех действий в сети требуется ETH.
- Невозможно восстановить средства в случае утраты приватного ключа.
EIP-3074 направлен на решение этих проблем и улучшение пользовательского опыта EOAs. Служа формой нативной абстракции аккаунтов, это обновление делает важный шаг на пути Ethereum к достижению цели — привлечения следующего миллиарда пользователей.
Мотивация за EIP-3074
Часто поступают запросы на расширение функциональности EOAs, включая добавление таких функций, как пакетная обработка, спонсорство газа, время истечения и скриптование. Однако такие дополнения могут усложнить и ужесточить протокол, увеличивая его уязвимость к атакам.
Этот EIP предлагает иной подход. Вместо того чтобы интегрировать эти функции непосредственно в протокол как часть требований к валидности транзакций, он позволяет пользователям делегировать контроль их EOA контракту. Этот подход предоставляет разработчикам гибкую основу для создания уникальных схем транзакций для EOAs, фактически позволяя EOA работать как кошелек смарт-контракта без необходимости развертывания самого контракта.
Хотя этот EIP предлагает значительные преимущества для отдельных пользователей, его основная мотивация заключается в «спонсируемых транзакциях». Это означает, что стоимость транзакции оплачивается другим аккаунтом, а не инициирующим её. Учитывая значительный рост количества токенов на Ethereum, стало обычным явлением, когда EOAs содержат ценные активы без наличия эфира. В настоящее время эти активы должны быть конвертированы в эфир для покрытия комиссий за газ. Однако без эфира для конверсии возникает тупиковая ситуация. Спонсируемые транзакции разрывают этот круг, позволяя другому аккаунту покрывать комиссии, что позволяет EOAs функционировать даже без наличия эфира.
Как это работает
Вводится новая контекстная переменная `authorized`. Эта переменная действительна только для текущего контекста выполнения (то есть для одного и того же контракта может быть несколько значений `authorized`, пока они остаются в отдельных иерархиях вызовов). Эта переменная хранит адрес аккаунта, который авторизовал (отсюда и название) данный контракт действовать от его имени в текущем контексте выполнения.
Следует отметить, что контекст выполнения изменяется при вложенных вызовах. Таким образом, вызов внутри вызова имеет контекст, отличный от контекста родительского вызова.
Установка контекстной переменной authorized
Для установки контекстной переменной `authorized` вводится новый опкод `AUTH` (0xf6). Этот опкод принимает адрес авторизатора (authority в спецификации) и указание на непрерывный диапазон памяти, в котором хранятся данные, необходимые для подтверждения авторизации (то есть данные ECDSA-подписи). Эти данные состоят из подписи строки с определенной структурой и необязательного слова EVM (32 байта) commit.
Следует отметить, что authority не должен быть контрактом; то есть его `EXTCODESIZE` должен быть нулевым. В противном случае операция завершается неудачно.
Любая неудача выполнения опкода `AUTH` сбрасывает значение переменной `authorized`, даже если она была установлена ранее в текущем контексте выполнения.
Строка, которая должна быть подписана (сообщение, на котором вычисляется подпись), формируется как конкатенация (здесь `||`):
```
MSG = keccak256(MAGIC || chainId || nonce || invokerAddress || commit)
```
Здесь:
- `MAGIC` — фиксированная строка байтов (0x04), предназначенная для предотвращения коллизий подписей.
- `chainId` — ID сети для использования в других EVM сетях. Дополнен до полного слова EVM.
- `nonce` — текущий nonce подписанта, дополнен до слова EVM слева.
- `invokerAddress` — адрес контракта, из которого выполняется опкод (то есть активный адрес состояния); дополнен до слова EVM слева.
- `commit` — значение commit, находящееся в памяти, как описано выше; оно предназначено для кодирования пользовательских условий, определяющих допустимость возможных транзакций, выполняемых от имени авторизованного контракта.
Этот опкод устанавливает контекстную переменную `authorized` равной authority, при условии, что адрес подписанта равен authority (как это определяется из подписи) и что подпись действительна. Возвращаемое значение `AUTH` — это индикатор успешности в виде булевого значения.
Принятый диапазон памяти для `AUTH` не фиксирован, как можно предположить из вышеописанного. Он динамичен для целей обновляемости; однако, все избыточные байты в настоящее время игнорируются.
Вызов из авторизованного контракта (AUTHCALL)
В дополнение введен еще один опкод: `AUTHCALL` (0xf7). Он представляет собой модификацию существующего опкода `CALL` (0xf1) и принимает те же аргументы (gas, addr, value, argsOffset, argsLength, retOffset, retLength). Суть этих аргументов очевидна из их названий. Однако, при необходимости, рекомендуется обратиться к информации о аргументах опкода `CALL`, так как они полностью совпадают.
Существует ряд логических различий в выполнении `AUTHCALL` по сравнению с `CALL`. Они следующие (упорядочены по приоритету):
1. **Адрес вызывающего абонента** в вызове — это `authorized`, если он не сброшен. В противном случае `AUTHCALL` завершается неудачно. Это механизм, с помощью которого контракт может быть представлен как EOA.
2. Если `gas` равен нулю, используется весь доступный газ.
3. Если доступный для подвызыва превышен `gas`, опкод завершится неудачно.
4. Газ для подвызыва рассчитывается по следующему псевдокоду:
```python
if gas == 0:
subcall_gas = remaining_gas — (remaining_gas // 64) # см. EIP-150 для обоснования
elif remaining_gas — (remaining_gas // 64) < gas:
OPCODE_FAILS
else:
subcall_gas = gas
```
5. Газовая стипендия в 2300, которыя обычно сопровождает вызовы с ненулевой ценностью, не выделяется.
6. **value вычитается** из баланса `authorized`, а не контракта.
7. **Значение `authorized`** остается неизменным при `AUTHCALL`.
Эти особенности делают `AUTHCALL` мощным инструментом для создания более гибких и безопасных взаимодействий с контрактами, используя авторизацию.
Как работает commit
Использование commit не является обязательным согласно EIP, и предполагается, что сообщество разработает стандарты по его применению (и будет внимательно следить за контрактами, которым они предоставляют авторизацию) самостоятельно, хотя предоставлен набор рекомендаций.
Основная цель commit — дать пользователю возможность закодировать спецификацию разрешенных вызовов или их свойств (например, путем вычисления хэша значений вызова, которые вызывающий должен проверить перед выполнением вызова). Поскольку commit подписан, его нельзя изменить без аннулирования подписи, если только частный ключ авторизатора не будет скомпрометирован.
EIP рекомендует создавать commit путем хеширования значений, которые вызывающий должен проверить. Следовательно, вызывающий должен обеспечить целостность ввода (т. е. что он авторизован пользователем), вычисляя хеш commit в качестве операции проверки.
Пример commit для многоразовой авторизации, взятый из текста EIP, приведен ниже (значения хешируются для получения поля commit):
```solidity
// Пример commit для многоразовой авторизации
// Хешируем значения вызовов для генерации commit
bytes32 commit = keccak256(abi.encodePacked(
toAddress1, value1, data1,
toAddress2, value2, data2,
…
));
// Авторизуем контракт на выполнение вызовов
AUTHCALL(
gas,
toAddress,
value,
argsOffset,
argsLength,
retOffset,
retLength,
commit
);
```
В данном примере commit создается путем хеширования данных, которые будут проверяться вызывающим. Это позволяет убедиться, что вызовы, которые выполняются контрактом, действительно авторизованы пользователем.
Это также означает, что управление EOA (Externally Owned Account) может быть произвольно делегировано через поле commit с любыми условиями, которые пользователь желает указать (см. пример на картинке ниже).
Пример использования commit для произвольного делегирования управления:
```solidity
// Пример делегирования управления с использованием commit
// Допустим, мы хотим ограничить контракт на выполнение определенных вызовов
address toAddress = 0xContractAddress;
uint256 value = 1 ether;
bytes memory data = abi.encodeWithSignature(“methodName(uint256)”, 42);
// Создаем commit, который включает ограничения вызова
bytes32 commit = keccak256(abi.encodePacked(
toAddress, value, data
));
// Авторизуем контракт с использованием commit
AUTHCALL(
gas,
toAddress,
value,
argsOffset,
argsLength,
retOffset,
retLength,
commit
);
```
В этом примере commit создается путем хеширования адреса назначения, значения и данных вызова. Это позволяет пользователю делегировать выполнение только определенных вызовов контракту, обеспечивая дополнительную безопасность и контроль над действиями, выполняемыми от имени EOA.
Последствия
EIP-3074 значительно расширяет функциональность EOAs (Externally Owned Accounts), позволяя произвольное делегирование без использования текущих инструментов абстракции аккаунтов. Это означает, что после разработки шаблона invoker-контракта, все решения по абстракции аккаунтов, кроме тех, у которых есть наиболее устойчивая экосистема, со временем исчезнут из-за превосходной простоты использования EIP-3074 (забавный факт: использование AUTHCALL вместо CALL для отправки нативных токенов будет дешевле, так как не предоставляется 2300 газов для инициализации счетчика газа).
Давление на существующие решения по абстракции аккаунтов будет только увеличиваться с течением времени, поскольку будут разрабатываться более сложные шаблоны invoker-контрактов. Например, решения, которые позволяют реализовать форму совместной подписи без использования дополнительных внецепочечных технологий.
Хотя необходимость доверять авторизованному invoker-контракту может показаться проблематичной, на самом деле это не отличается от использования существующих решений, которые также требуют доверия к их правильной работе.
Кроме того, этот EIP введет on-chain определенную форму условности транзакций из EOA, которая не требует существования off-chain компонентов для мониторинга, что, безусловно, расширит возможности автоматизации.
Заключение
EIP-3074 ставит значительный рубеж в эволюции Ethereum в сторону более ориентированного на пользователя, доступного и экономически эффективного будущего. Позволяя возможность делегирования управления EOA контракту-инвокеру, это обновление открывает новые возможности, такие как пакетные и спонсорские транзакции, возможность восстановления средств в случае утери закрытых ключей и многое другое.
Эти достижения не только обеспечат необходимый путь к нативной абстракции аккаунтов, оптимизируя взаимодействие пользователей, но и расширят спектр применения для надежной и децентрализованной on-chain автоматизации.
Twitter | Discord | YouTube |Telegram | CMC Community | Debank | Medium