Skip to content

Commit f9d449d

Browse files
committed
article: ERC-1271
1 parent 2110709 commit f9d449d

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

EIPs/erc-1271/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# ERC-1271: Standard Signature Validation Method for Contracts
2+
3+
**Автор:** [Павел Найданов](https://github.com/PavelNaydanov) 🕵️‍♂️
4+
5+
Стандарт ERC-1271 предлагает универсальный интерфейс для проверки подписи. Смарт-контракт, который реализует подобный интерфейс, будет считаться сайнером (Signer) и подтверждать валидность подписи.
6+
7+
Это ключевая идея стандарта — позволить контрактам выступать в роли `signer'ов`, несмотря на отсутствие у них приватных ключей для генерации подписей. Вместо этого смарт-контракт предоставляет метод `isValidSignature()`, через который внешние протоколы и приложения могут запросить проверку валидности подписи.
8+
9+
![](./images/erc-1271-flow.png)
10+
11+
На схеме видно, что EOA, который является владельцем смарт-контракта **Wallet**, подписывает транзакцию и приложение передает вместе с вызовом его подпись на смарт-контракт **Target**, который может представлять биржу, лендинг протокол и другие DApps. **Target** валидирует подпись EOA на смарт-контракте **Wallet**, который выступает `Signer'ом` в рамках стандарта ERC-1271.
12+
13+
_Важно!_ Стоит отметить, что в нашем случае подпись сгенерирована приватным ключом EOA, но могут быть использованы и другие типы проверок.
14+
15+
Смарт-контракт Wallet должен реализовать функцию:
16+
```solidity
17+
function isValidSignature(bytes32 _hash, bytes calldata _signature) external view returns (bytes4)
18+
```
19+
20+
Это `view` функция, которая не должна изменять состояние. Также стоит обратить внимание на возвращаемое значение: в случае успеха возвращается `bytes4` - это селектор функции `isValidSignature()` (селектор самой себя). Это классический подход, когда возвращается не булевое значение. Возвращаемые `bytes4` вносят больше определенности, так как смарт-контракт не поддерживающий EIP-1271, при вызове у него функции `isValidSignature()`, может провалиться в `fallback()` функцию и вернуть **true** непреднамеренно.
21+
22+
Пример кода ниже взят из спецификации [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271).
23+
24+
```solidity
25+
26+
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
27+
bytes4 constant internal MAGICVALUE = 0x1626ba7e;
28+
29+
/**
30+
* @dev Should return whether the signature provided is valid for the provided hash
31+
* @param _hash Hash of the data to be signed
32+
* @param _signature Signature byte array associated with _hash
33+
*
34+
* MUST return the bytes4 magic value 0x1626ba7e when function passes.
35+
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
36+
* MUST allow external calls
37+
*/
38+
function isValidSignature(
39+
bytes32 _hash,
40+
bytes memory _signature)
41+
public
42+
view
43+
returns (bytes4 magicValue);
44+
}
45+
```
46+
47+
_Важно!_ Реализация функции `isValidSignature()` не регламентируется строго и может содержать сложную реализацию проверки подписи под капотом, которая зависит:
48+
- от контекста (например, от времени или состояния)
49+
- от EOA (например, от уровня авторизации подписывающего в смарт-кошельке)
50+
- от схемы подписи (например, ECDSA, multisig, BLS) и т.д.
51+
52+
Поэтому некоторые реализации могут потреблять больше газа, чем ожидалось. Важно не задавать количество газа, отправляемого при вызове функции с Target контракта, так как это может привести к провалу транзакции.
53+
54+
По сути, это все что необходимо знать про этот стандарт.
55+
56+
## Проблемы с ERC-1271
57+
58+
Множество протоколов которые решают проблемы абстракции аккаунтов поддерживают ERC-1271 под капотом своих смарт-контрактов, реализующих абстрактные аккаунты.
59+
60+
В 2023 года компания Alchemy обнаружила уязвимость в своем смарт-контракте `LightAccount`. Проблема заключалась в возможности воспроизведения подписи ERC-1271 повторно. EOA, которые завели себе несколько `LightAccount`, давая подпись для одного из них, автоматически разрешали применить подпись и ко второму `LightAccount`.
61+
62+
![](./images/erc-1271-replay-attack.png)
63+
64+
На схеме видно, что с одной и той же подписью `Recipient` может получить два транша с разных аккаунтов пользователя.
65+
66+
Подобная проблема оказалась еще у целого ряда АА протоколов: Zerodev, Biconomy, Soul Wallet, EIP4337Fallback для Gnosis Safes от eth-infinitism, AmbireAccount, SmartAccount от OKX, BaseWallet от Argent и Fuse Wallet.
67+
68+
Проблема касалась случаев, когда подпись разрешала смарт-контрактам Permit2 перевести токены. Также сюда можно отнести другие подобные смарт-контракты, например, Cowswap, Lens протокол и другие.
69+
70+
Ребята из Alchemy забили тревогу и совместно с другими специалистами по АА выработали два решение.
71+
72+
**Решение 1. Использовать domain из EIP-712**
73+
74+
При помощи структуры domain, как в [EIP-712](https://eips.ethereum.org/EIPS/eip-712), определить на каком смарт-контракте можно проверить подпись.
75+
76+
```solidity
77+
function isValidSignature(bytes32 digest, bytes calldata sig) external view returns (bytes4) {
78+
bytes32 domainSeparator =
79+
keccak256(
80+
abi.encode(
81+
_DOMAIN_SEPARATOR_TYPEHASH,
82+
_NAME_HASH,
83+
_VERSION_HASH,
84+
block.chainid,
85+
address(walletA) // Определяет на каком смарт-контракте кошельке может быть валидирована подпись
86+
)
87+
);
88+
89+
bytes32 wrappedDigest = keccak256(abi.encode("\x19\x01", domainSeparator, digest));
90+
91+
return ECDSA.recover(wrappedDigest, sig);
92+
}
93+
```
94+
95+
**Решение 2. Добавлять адрес смарт-контракта к хешу данных**
96+
97+
Используется хеш подписи и к ней добавляется адрес смарт-контракта на котором можно валидировать подпись.
98+
Это решение более легковесно, но означает, что клиентам кошельков придётся отображать непрозрачный хеш для подписи пользователей.
99+
100+
```solidity
101+
function isValidSignature(bytes32 digest, bytes calldata sig) external view returns (bytes4) {
102+
bytes32 wrappedDigest = keccak256(abi.encode(digest, address(SCA));
103+
return ECDSA.recover(wrappedDigest, sig);
104+
}
105+
```
106+
107+
## Вывод
108+
109+
ERC-1271 - это очень простой ERC, но он имеет решающее значение для установления стандарта проверки подписей с помощью смарт-контрактов.
110+
111+
Многие думают, что стандарт - это возможность смарт-контрактам давать разрешения через подпись от своего имени. Но я бы посмотрел еще шире, на мой взгляд этот стандарт можно применить в любом случае, когда необходимо проверять подпись на смарт-контрактах. Поэтому, если ваши смарт-контракты реализуют любую безгазовость через подписи или проверяют комиссии установленные бекендом и так далее, то смело можно использовать этот интерфейс от ERC-1271.
112+
113+
## Links
114+
115+
1. [ERC-1271: Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271)
116+
2. [ERC-1271 Signature Replay Vulnerability](https://www.alchemy.com/blog/erc-1271-signature-replay-vulnerability)
117+
3. [Clarifying ERC-1271: Smart Contract Signature Verification](https://medium.com/taipei-ethereum-meetup/clarifications-on-erc-1271-smart-contract-signature-verification-and-signing-cd5c2fb7ac1b)
54.1 KB
Loading
73.5 KB
Loading

0 commit comments

Comments
 (0)