EIP-1271: Podepisování a ověřování podpisů chytrých kontraktů
Standard EIP-1271 (opens in a new tab) umožňuje chytrým kontraktům ověřovat podpisy.
V tomto tutoriálu poskytneme přehled digitálních podpisů, pozadí EIP-1271 a konkrétní implementaci EIP-1271, kterou používá Safe (opens in a new tab) (dříve Gnosis Safe). To vše dohromady může posloužit jako výchozí bod pro implementaci EIP-1271 ve vašich vlastních kontraktech.
Co je to podpis?
V tomto kontextu je podpis (přesněji „digitální podpis“) zpráva plus nějaký druh důkazu, že zpráva pochází od konkrétní osoby/odesílatele/adresy.
Digitální podpis může vypadat například takto:
- Zpráva: „Chci se přihlásit na tuto webovou stránku pomocí své Ethereum peněženky.“
- Podepisující: Moje adresa je
0x000… - Důkaz: Zde je nějaký důkaz, že já,
0x000…, jsem skutečně vytvořil celou tuto zprávu (obvykle se jedná o něco kryptografického).
Je důležité si uvědomit, že digitální podpis zahrnuje jak „zprávu“, tak „podpis“.
Proč? Pokud byste mi například dali podepsat kontrakt a já bych pak odstřihl stránku s podpisem a vrátil vám pouze své podpisy bez zbytku kontraktu, kontrakt by nebyl platný.
Stejně tak digitální podpis neznamená nic bez přidružené zprávy!
Proč existuje EIP-1271?
Abyste mohli vytvořit digitální podpis pro použití na blockchainech založených na Ethereu, obecně potřebujete tajný soukromý klíč, který nikdo jiný nezná. To je to, co dělá váš podpis vaším (nikdo jiný nemůže vytvořit stejný podpis bez znalosti tajného klíče).
Váš Ethereum účet (tj. váš externě vlastněný účet/EOA) má k sobě přidružený soukromý klíč a to je soukromý klíč, který se obvykle používá, když vás webová stránka nebo decentralizovaná aplikace (dapp) požádá o podpis (např. pro „Přihlášení pomocí Etherea“).
Aplikace může ověřit podpis (opens in a new tab), který vytvoříte pomocí knihovny třetí strany, jako je Ethers.js, aniž by znala váš soukromý klíč (opens in a new tab), a mít jistotu, že vy jste byli tím, kdo podpis vytvořil.
Ve skutečnosti, protože digitální podpisy EOA používají kryptografii veřejného klíče, mohou být generovány a ověřovány offchain! Takto funguje hlasování v DAO bez poplatků za gas – místo odesílání hlasů onchain lze digitální podpisy vytvářet a ověřovat offchain pomocí kryptografických knihoven.
Zatímco účty EOA mají soukromý klíč, účty chytrých kontraktů nemají žádný druh soukromého nebo tajného klíče (takže „Přihlášení pomocí Etherea“ atd. nemůže nativně fungovat s účty chytrých kontraktů).
Problém, který se EIP-1271 snaží vyřešit: jak můžeme poznat, že je podpis chytrého kontraktu platný, pokud chytrý kontrakt nemá žádné „tajemství“, které by mohl do podpisu začlenit?
Jak funguje EIP-1271?
Chytré kontrakty nemají soukromé klíče, které by bylo možné použít k podepisování zpráv. Jak tedy můžeme poznat, zda je podpis autentický?
Jedním z nápadů je, že se můžeme chytrého kontraktu jednoduše zeptat, zda je podpis autentický!
To, co EIP-1271 dělá, je, že standardizuje tuto myšlenku „dotazování“ chytrého kontraktu, zda je daný podpis platný.
Kontrakt, který implementuje EIP-1271, musí mít funkci nazvanou isValidSignature, která přijímá zprávu a podpis. Kontrakt pak může spustit nějakou ověřovací logiku (specifikace zde nevynucuje nic konkrétního) a poté vrátit hodnotu indikující, zda je podpis platný, či nikoli.
Pokud isValidSignature vrátí platný výsledek, je to v podstatě kontrakt, který říká „ano, schvaluji tento podpis + zprávu!“
Rozhraní
Zde je přesné rozhraní ve specifikaci EIP-1271 (o parametru _hash budeme mluvit níže, ale prozatím si ho představte jako zprávu, která se ověřuje):
pragma solidity ^0.5.0;
contract ERC1271 {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 constant internal MAGICVALUE = 0x1626ba7e;
/**
* @dev Měl by vrátit, zda je poskytnutý podpis platný pro poskytnutý hash
* @param _hash Hash dat k podepsání
* @param _signature Bajtové pole podpisu přidružené k _hash
*
* MUSÍ vrátit magickou hodnotu bytes4 0x1626ba7e, pokud funkce projde.
* NESMÍ měnit stav (pomocí STATICCALL pro solc < 0.5, modifikátoru view pro solc > 0.5)
* MUSÍ umožňovat externí volání
*/
function isValidSignature(
bytes32 _hash,
bytes memory _signature)
public
view
returns (bytes4 magicValue);
}
Příklad implementace EIP-1271: Safe
Kontrakty mohou implementovat isValidSignature mnoha způsoby – specifikace o přesné implementaci příliš nemluví.
Jedním z významných kontraktů, který implementuje EIP-1271, je Safe (dříve Gnosis Safe).
V kódu Safe je implementována (opens in a new tab) funkce isValidSignature tak, že podpisy lze vytvářet a ověřovat dvěma způsoby (opens in a new tab):
- Onchain zprávy
- Vytvoření: vlastník Safe vytvoří novou Safe transakci k „podepsání“ zprávy, přičemž zprávu předá jako data do transakce. Jakmile transakci podepíše dostatek vlastníků k dosažení prahu multisig, transakce je odeslána do sítě a spuštěna. V transakci je volána funkce Safe (
signMessage(bytes calldata _data)), která přidá zprávu do seznamu „schválených“ zpráv. - Ověření: zavolejte
isValidSignaturena kontraktu Safe a předejte zprávu k ověření jako parametr zprávy a prázdnou hodnotu pro parametr podpisu (opens in a new tab) (tj.0x). Safe uvidí, že parametr podpisu je prázdný, a místo kryptografického ověřování podpisu bude vědět, že má jednoduše zkontrolovat, zda je zpráva na seznamu „schválených“ zpráv.
- Vytvoření: vlastník Safe vytvoří novou Safe transakci k „podepsání“ zprávy, přičemž zprávu předá jako data do transakce. Jakmile transakci podepíše dostatek vlastníků k dosažení prahu multisig, transakce je odeslána do sítě a spuštěna. V transakci je volána funkce Safe (
- Offchain zprávy:
- Vytvoření: vlastník Safe vytvoří zprávu offchain a poté získá další vlastníky Safe, aby zprávu každý individuálně podepsal, dokud nebude dostatek podpisů k překonání prahu pro schválení multisig.
- Ověření: zavolejte
isValidSignature. V parametru zprávy předejte zprávu, která má být ověřena. V parametru podpisu předejte jednotlivé podpisy každého vlastníka Safe spojené dohromady, jeden za druhým. Safe zkontroluje, zda je dostatek podpisů ke splnění prahu a zda je každý podpis platný. Pokud ano, vrátí hodnotu indikující úspěšné ověření podpisu.
Co přesně je parametr _hash? Proč nepředat celou zprávu?
Možná jste si všimli, že funkce isValidSignature v rozhraní EIP-1271 (opens in a new tab) nepřijímá samotnou zprávu, ale místo toho parametr _hash. To znamená, že místo předání celé zprávy libovolné délky funkci isValidSignature předáme 32bajtový hash zprávy (obecně keccak256).
Každý bajt dat volání – tj. dat parametrů funkce předaných funkci chytrého kontraktu – stojí 16 gas (4 gas, pokud jde o nulový bajt) (opens in a new tab), takže to může ušetřit spoustu gas, pokud je zpráva dlouhá.
Předchozí specifikace EIP-1271
V praxi existují specifikace EIP-1271, které mají funkci isValidSignature s prvním parametrem typu bytes (libovolná délka, místo pevné délky bytes32) a názvem parametru message. Jedná se o starší verzi (opens in a new tab) standardu EIP-1271.
Jak by měl být EIP-1271 implementován v mých vlastních kontraktech?
Specifikace je zde velmi otevřená. Implementace Safe má několik dobrých nápadů:
- Můžete považovat podpisy EOA od „vlastníka“ kontraktu za platné.
- Mohli byste ukládat seznam schválených zpráv a považovat za platné pouze ty.
Nakonec je to na vás jako na vývojáři kontraktu!
Závěr
EIP-1271 (opens in a new tab) je všestranný standard, který umožňuje chytrým kontraktům ověřovat podpisy. Otevírá dveře k tomu, aby se chytré kontrakty chovaly více jako EOA – například poskytuje způsob, jak může „Přihlášení pomocí Etherea“ fungovat s chytrými kontrakty – a lze jej implementovat mnoha způsoby (Safe má netriviální, zajímavou implementaci, která stojí za zvážení).