Перейти до основного контенту

EIP-1271: Підписання та верифікація підписів смарт-контрактів

eip-1271
Смарт-контракти
верифікація
підписання
Середнячок
Nathan H. Leung
12 січня 2023 р.
5 читається за хвилину

Стандарт EIP-1271 (opens in a new tab) дозволяє смарт-контрактам перевіряти підписи.

У цьому посібнику ми надаємо огляд цифрових підписів, передісторії EIP-1271, а також конкретної реалізації EIP-1271, яку використовує Safe (opens in a new tab) (раніше Gnosis Safe). Все разом це може слугувати початковим етапом для імплементації EIP-1271 у ваші власні контракти.

Що таке підпис?

У цьому контексті підпис (точніше, "цифровий підпис") - це саме повідомлення плюс певний доказ того, що повідомлення надійшло від конкретної особи/відправника/адреси.

Наприклад, цифровий підпис може виглядати так:

  1. Повідомлення: "Я хочу увійти на цей сайт за допомогою свого гаманця Ethereum".
  2. Підписант: Моя адреса — 0x000…
  3. Доказ: Ось доказ того, що я, 0x000…, насправді створив усе це повідомлення (зазвичай це щось криптографічне).

Важливо зазначити, що цифровий підпис включає в себе як "повідомлення", так і "підпис".

Чому? Наприклад, якщо ви дали мені контракт на підпис, а потім я відрізав сторінку з підписами і віддав вам тільки свої підписи без решти контракту, контракт буде недійсним.

Так само і цифровий підпис нічого не означає без пов'язаного з ним повідомлення!

Для чого потрібен EIP-1271?

Щоб створити цифровий підпис для використання в блокчейнах на основі Ethereum, вам, як правило, потрібен секретний приватний ключ, який не знає більше ніхто. Це те, що робить ваш підпис вашим (ніхто інший не може створити такий самий підпис без знання секретного ключа).

З вашим обліковим записом Ethereum (тобто зовнішнім обліковим записом/EOA) пов'язаний приватний ключ, і саме цей приватний ключ зазвичай використовується, коли вебсайт або dapp просить у вас підпис (наприклад, для "Входу за допомогою Ethereum").

Застосунок може перевірити підпис (opens in a new tab), створений вами за допомогою сторонньої бібліотеки, як-от ethers.js, не знаючи вашого приватного ключа (opens in a new tab) і бути впевненим, що саме ви створили цей підпис.

Насправді, оскільки цифрові підписи EOA використовують криптографію з відкритим ключем, їх можна генерувати й перевіряти поза мережею! Саме так працює безгазове голосування DAO — замість того, щоб надсилати голоси в мережі, цифрові підписи можна створювати та перевіряти поза мережею за допомогою криптографічних бібліотек.

У той час як облікові записи EOA мають приватний ключ, облікові записи смарт-контрактів не мають ніякого приватного або секретного ключа (тому такі транзакції як "Авторизація за допомогою Ethereum" і т.д. не можуть працювати з обліковими записами смарт-контрактів).

Проблема, яку намагається вирішити EIP-1271: як ми можемо визначити, що підпис смарт-контракту дійсний, якщо смарт-контракт не має "секрету", який він може включити в підпис?

Як працює EIP-1271?

Смарт-контракти не мають приватних ключів, які можна використовувати для підписання повідомлень. Тож як ми можемо визначити, чи є підпис справжнім?

Що ж, одна з ідей полягає в тому, що ми можемо просто запитати смарт-контракт, чи є підпис автентичним!

EIP-1271 стандартизує цю ідею, "запитуючи" смарт-контракт, чи дійсний даний підпис.

Контракт, який реалізує EIP-1271, повинен мати функцію isValidSignature, яка приймає повідомлення та підпис. Після цього контракт може виконати певну логіку перевірки (специфікація не передбачає нічого конкретного), а потім повернути значення, яке вказує на те, чи дійсний підпис, чи ні.

Якщо isValidSignature повертає дійсний результат, це, по суті, означає, що контракт каже: "так, я схвалюю цей підпис + повідомлення!"

Інтерфейс

Ось точний інтерфейс у специфікації EIP-1271 (ми поговоримо про параметр _hash нижче, але наразі вважайте його повідомленням, що перевіряється):

1pragma solidity ^0.5.0;
2
3contract ERC1271 {
4
5 // bytes4(keccak256("isValidSignature(bytes32,bytes)")
6 bytes4 constant internal MAGICVALUE = 0x1626ba7e;
7
8 /**
9 * @dev Має повертати, чи наданий підпис є дійсним для наданого хешу
10 * @param _hash Хеш даних, що підписуються
11 * @param _signature Масив байтів підпису, пов'язаний із _hash
12 *
13 * ПОВИННА повертати магічне значення bytes4 0x1626ba7e, коли функція проходить перевірку.
14 * НЕ ПОВИННА змінювати стан (використовуючи STATICCALL для solc < 0.5, модифікатор view для solc > 0.5)
15 * ПОВИННА дозволяти зовнішні виклики
16 */
17 function isValidSignature(
18 bytes32 _hash,
19 bytes memory _signature)
20 public
21 view
22 returns (bytes4 magicValue);
23}
Показати все

Приклад реалізації EIP-1271: Safe

Контракти можуть реалізовувати isValidSignature різними способами — у специфікації мало що сказано про точну реалізацію.

Одним з важливих контрактів, який реалізує EIP-1271, є контракт Safe (раніше Gnosis Safe).

У коді Safe функція isValidSignature реалізована (opens in a new tab) так, що підписи можна створювати та перевіряти двома способами (opens in a new tab):

  1. Повідомлення в мережі
    1. Створення: власник safe створює нову безпечну транзакцію для "підписання" повідомлення, передаючи повідомлення як дані в транзакцію. Як тільки достатня кількість власників підписує транзакцію, щоб досягти порогу мультипідпису, транзакція транслюється і виконується. У транзакції є функція Safe (signMessage(bytes calldata _data)), яка додає повідомлення до списку "затверджених" повідомлень.
    2. Перевірка: викличте isValidSignature для контракту Safe і передайте повідомлення для перевірки як параметр повідомлення та порожнє значення для параметра підпису (opens in a new tab) (тобто 0x). Safe побачить, що параметр підпису порожній, і замість того, щоб криптографічно перевірити підпис, він просто перевірить, чи є повідомлення у списку "схвалених" повідомлень.
  2. Повідомлення поза мережею:
    1. Створення: власник Safe створює повідомлення поза мережею, потім збирає підписи інших власників Safe під цим повідомленням, доки не набереться достатня кількість підписів для подолання порогу схвалення мультипідписом.
    2. Перевірка: викличте isValidSignature. У параметрі message передайте повідомлення, яке потрібно перевірити. У параметрі signature введіть індивідуальні підписи кожного власника safe, об'єднані разом, один за одним. Safe перевірить, чи достатньо підписів для досягнення порогу і чи кожен підпис є дійсним. Якщо так, він поверне значення, що свідчить про успішну перевірку підпису.

Що саме являє собою параметр _hash? Чому б не передавати повідомлення повністю?

Ви могли помітити, що функція isValidSignature в інтерфейсі EIP-1271 (opens in a new tab) приймає не саме повідомлення, а параметр _hash. Це означає, що замість передачі повного повідомлення довільної довжини в isValidSignature, ми натомість передаємо 32-байтовий хеш повідомлення (зазвичай keccak256).

Кожен байт calldata — тобто даних параметрів, що передаються у функцію смарт-контракту — коштує 16 газу (4 гази, якщо це нульовий байт) (opens in a new tab), тож це може заощадити багато газу, якщо повідомлення довге.

Попередні специфікації EIP-1271

На практиці існують специфікації EIP-1271, що мають функцію isValidSignature із першим параметром типу bytes (довільної довжини замість фіксованої bytes32) та назвою параметра message. Це старіша версія (opens in a new tab) стандарту EIP-1271.

Як впроваджувати EIP-1271 у моїх власних контрактах?

Специфікація тут дуже широка. У впровадженні Safe є кілька хороших ідей:

  • Ви можете вважати підписи EOA від "власника" контракту дійсними.
  • Ви можете зберігати список схвалених повідомлень і вважати дійсними лише їх.

Зрештою, це залежить від вас як від розробника контракту!

Висновок

EIP-1271 (opens in a new tab) — це універсальний стандарт, який дозволяє смарт-контрактам перевіряти підписи. Це відкриває двері для смарт-контрактів, щоб вони діяли більше як EOA - наприклад, надаючи можливість "Авторизуватися за допомогою Ethereum" для роботи зі смарт-контрактами - і це може бути реалізовано багатьма способами (Safe має нетривіальну, цікаву реалізацію, яку варто розглянути).

Останні оновлення сторінки: 16 січня 2026 р.

Чи була ця інструкція корисною?