メインコンテンツへスキップ

EIP-1271: スマートコントラクト署名に対する署名と検証

eip-1271スマートコントラクト検証署名(signing)
中級
Nathan H. Leung
2023年1月12日
11 分の読書 minute read

EIP-1271(opens in a new tab)標準は、スマートコントラクトで署名の検証を可能にします。

このチュートリアルでは、デジタル署名、EIP-1271の背景、EIP-1271の具体的な実装であるSafe(opens in a new tab) (旧Gnosis Safe) の概要を説明します。 このチュートリアル全体を通して、EIP-1271を自身のコントラクトへ実装するための出発点として使うことができます。

「署名」とは何か

このチュートリアルにおいて、署名 (正確には「デジタル署名」) とは、特定の人物/送信者/アドレスから送信されたものであることを示す何らかの証拠をそのメッセージに対して加えたものです。

具体的には、デジタル署名は次のようなものです。

  1. メッセージ: 「イーサリアムウォレットを使用してこのWebサイトにログインしたい」
  2. 署名者: 私のアドレスは、0x000…です。
  3. 証拠: ここに、私「0x000…」がこのメッセージ全体を実際に作成したという証拠 (これは通常暗号化される) があります。

デジタル署名には、「メッセージ」と「署名」の両方が含まれていることが重要です。

それはなぜでしょうか? 具体的には、あなたが私に署名するように契約書を渡した後、私が署名のあるページを切り取り、契約事項のない署名だけを返した場合、その契約は無効なることがあげられます。

同様にデジタル署名においても、それに紐づいたメッセージがなければ意味がありません。

EIP-1271が必要な理由

イーサリアムベースのブロックチェーン上でデジタル署名を作成するには、通常、他の人が知らない秘密鍵が必要になります。 秘密鍵により、あなたの署名が自身のものになります (誰も秘密鍵を知らない限り、同じ署名を作成できません) 。

イーサリアムアカウント (すなわち、外部所有アカウント/EOA) には、アカウントに紐づいた秘密鍵があります。この秘密鍵が、通常、ウェブサイトやDappで求められる署名に使われます (例:「イーサリアムでログイン」) 。

アプリでは、ethers.jsのようなサードパーティライブラリを使って秘密鍵を知らせることなく(opens in a new tab)、作成した署名を検証でき(opens in a new tab)、署名を作成したのはあなたであると確信することができます。

事実として、EOA (外部所有アカウント) のデジタル署名は公開鍵暗号技術を使っているため、オフチェーンで生成および検証できます。 これは、ガスレスDAO投票の仕組みです。オンチェーンで投票を送信する代わりに、暗号化ライブラリを使用してオフチェーンでデジタル署名を作成し、検証可能です。

EOAの各アカウントには秘密鍵がありますが、スマートコントラクトアカウントにはいかなる種類の共通鍵も秘密鍵もありません (そのため、「イーサリアムでログイン」などはスマートコントラクトのアカウントにおいては、ネイティブで機能しません) 。

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 Should return whether the signature provided is valid for the provided hash
10 * @param _hash Hash of the data to be signed
11 * @param _signature Signature byte array associated with _hash
12 *
13 * MUST return the bytes4 magic value 0x1626ba7e when function passes.
14 * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
15 * MUST allow external calls
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を実装した有名なコントラクトの1つにSafe (旧Gnosis Safe) があります。

Safeのコードでは、署名の作成および検証において、次の2つの方法(opens in a new tab)を取れるようにisValidSignature実装されています(opens in a new tab)

  1. オンチェーンメッセージ
    1. 作成: Safeの所有者は、メッセージに「署名」するための新しいSafeのトランザクションを作成し、メッセージをデータとしてトランザクションに渡します。 十分な数の所有者がトランザクションに署名してマルチシグのしきい値に達すると、トランザクションがブロードキャストされて実行されます。 このトランザクションでは、「承認された」メッセージのリストに追加するSafe関数が呼び出されます。
    2. 検証: Safeコントラクトでは、isValidSignatureを呼び出し、検証するメッセージをメッセージパラメータとして、署名パラメータを空の値(opens in a new tab) (すなわち、0x) にして渡します。 Safeは、署名パラメータが空であることを確認し、署名を暗号的に検証する代わりに、メッセージが「承認された」メッセージのリストに存在するかのみを確認します。
  2. オフチェーンメッセージ
    1. 作成: Safeの所有者は、オフチェーンでメッセージを作成し、他のSafeの所有者に、マルチシグの承認のしきい値を超えるのに十分な署名が得られるまでメッセージに対して個別に署名してもらいます。
    2. 検証: isValidSignatureを呼び出します。 メッセージパラメータの中に、検証するメッセージを渡します。 署名パラメーターでは、Safe所有者各自の署名をすべて連結して、一斉に渡します。 Safeでは、しきい値を満たすのに十分な署名があることおよび各署名が有効であることをチェックします。 有効であれば、署名の検証が成功したことを示す値を返します。

_hashパラメータは、正確には何なのか およびメッセージ全体を渡さない理由

あなたは、EIP-1271インターフェース(opens in a new tab)isValidSignature関数がメッセージ自体を取らない代わりに _hashパラメーターを取ることに気付いたかもしれません。 これは、任意の長さのメッセージ全体を isValidSignatureに渡す代わりに、そのメッセージの32バイトのハッシュ (通常はkeccak256) を渡しています。

コールデータの各バイト、すなわち、スマートコントラクトへ渡される関数パラメータのデータには、16ガスのコストがかかります(opens in a new tab)(0バイトの場合は4ガス) 。 そのため、メッセージが長ければガスを大幅に節約できます。

以前のEIP-1271仕様

すでに出回っているEIP-1271仕様に、最初のパラメータでパラメータ名がmessageで、bytes型 (固定長のbytes32ではなく、任意の長さ) のisValidSignature関数があります。 これは、古いバージョン(opens in a new tab)のEIP-1271標準です。

EIP-1271を各自のコントラクトにどのように実装すべきか

実装は自由にできる仕様になっています。 Safeの実装には、次のいくつかの優れたアイデアがあります。

  • コントラクト「所有者」からのEOA署名を有効とみなせる
  • 承認されたメッセージのリストを保存し、それらのメッセージのみを有効であるとみなせる

最後に、実装はスマートコントラクトデベロッパーであるあなた次第です!

まとめ

EIP-1271(opens in a new tab)は、スマートコントラクトによる署名の検証を可能する標準で、多くの用途があります。 EIP-1271により、スマートコントラクトをEOAのように動作させることができます。例えば、「イーサリアムでログイン」をスマートコントラクトで動作させる方法を提供できます。また、さまざまな方法で実装できます (考慮するのに興味深い重要な実装としてSafeがあります) 。

最終編集者: @nhsz(opens in a new tab), 2023年8月15日

このチュートリアルは役に立ちましたか?