はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

今回は、EIP712の署名機能を活用することで、個人情報をブロックチェーンに保存せずに、安全かつ再利用可能な本人確認などの証明をオフチェーンで行える仕組みを提案しているERC1812についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

他にも様々なEIPについてまとめています。

概要

ERC1812は、「EIP712」に基づいた署名済みTyped Data(型付きデータ)を使って、オフチェーンで再利用可能な「Verifiable Claims(検証可能な証明)」を発行・提示できる仕組みです。

EIP712については以下の記事を参考にしてください。

Verifiable Claims とは、ある主体(たとえばKYCプロバイダーや政府機関など)が「この人は20歳以上である」、「この人は居住者である」などの証明を発行し、他の人がその内容を検証できる仕組みを指します。 ERC1812では、この証明を「EIP712」に準拠した形でオフチェーンに保存しつつ、スマートコントラクトやState Channel、あるいは外部ライブラリで検証できるようにします。

ユーザーは自分のWeb3ウォレットを使って簡単に署名付きの証明を発行でき、それをオフチェーンで保管して必要なときに提示できます。 これにより、オンチェーンに個人情報を残すことなく、コスト効率の高い形で信頼性のある証明が可能になります。

動機

現在、スマートコントラクトを現実の制度と接続するには「本人確認(KYC)」や「規制への準拠(GDPR、適格投資家認定など)」といったプロセスが不可欠です。 これを実現するために、「検証可能な証明(Verifiable Claims)」は非常に重要な役割を果たします。

これまでの標準である「ERC735」や「ERC780」は、ブロックチェーン上にこうした証明を保存する方式でした。 これは一部のユースケースでは有効ですが、多くのケースでは問題があります。 特にEUのGDPRのような規制では、パーソナルな情報(PII)をパブリックなブロックチェーンに記録すること自体が法的に認められない場合があります。

ERC735については以下の記事を参考にしてください。

ERC780については以下の記事を参考にしてください。

このような背景から、「オフチェーンに保存でき、かつ信頼性のある証明」が求められています。 既存の「W3C Verifiable Claims」や「uPort」などのソリューションもありますが、これらはJSON-LDやJWTといった形式に基づいており、Ethereumエコシステムにそのまま統合するのが難しいという課題があります。

そこで注目されるのが「EIP712」です。 これはSolidityのABIエンコーディングに基づいた署名形式を提供し、EthereumウォレットやWeb3クライアントでのサポートも進んでいます。 この形式を用いれば、オンチェーンのスマートコントラクトでも簡単にデータの内容と署名を検証できるため、軽量かつ再利用可能なVerifiable Claimsの実現が可能になります。

この仕組みにより、ユーザーは必要なときにだけ証明を提示でき、発行された証明はさまざまなスマートコントラクトで再利用可能になります。 つまり、「セキュアに・法的に安全で・開発者フレンドリー」なアイデンティティ証明の新しい形が実現されようとしています。

仕様

Claim(証明)の定義

Verifiable Claim(検証可能な証明)とは、「ある発行者(Issuer)が、ある対象(Subject)について、特定の属性や値を持っていると主張すること」です。

ERC1812では、こうしたClaimをEIP712の型付きデータ構造に基づいて表現します。 同じ発行者が同じ内容のClaimを複数回発行しても、結果が常に同一になるように「決定論的(deterministic)」である必要があります。

Claimの構造

すべてのClaimは、用途に応じた構造(Struct)で定義されますが、最低限以下の3つのフィールドを持つ必要があります。

  • subject 対象のEthereumアドレス。誰についてのClaimかを示します。
  • validFrom Claimの有効開始時刻(秒単位のUnixタイムスタンプ)。
  • validTo Claimの有効終了時刻。 同様にUnixタイムスタンプ。 期限なしにしたい場合は uint256 の最大値(0xffff...ffff)を使います。

例えば「あるアドレスを知っている」というClaimは、以下のような構造になります。

solidity
struct Know {
  address subject;
  uint256 validFrom;
  uint256 validTo;
}

Claimの検証方法(Verifying Contract)

Claimをスマートコントラクト内で検証するためには、verify()関数を持つ専用の検証用コントラクトを定義します。 例えば以下のように、署名の検証とタイムスタンプのチェックを行います。

solidity
function verifyIssuer(Know memory claim, uint8 v, bytes32 r, bytes32 s) public returns (address) {
  bytes32 digest = keccak256(
    abi.encodePacked(
      "\x19\x01",
      DOMAIN_SEPARATOR,
      hash(claim)
    )
  );
  require(
    (claim.validFrom <= block.timestamp) && (block.timestamp < claim.validTo),
    "invalid issuance timestamps"
  );
  return ecrecover(digest, v, r, s);
}

Solidity関数での使用例

スマートコントラクトの中でClaimを検証し、その結果を記録するような関数は以下のように書けます。

solidity
function vouch(Know memory claim, uint8 v, bytes32 r, bytes32 s) public returns (bool) {
  address issuer = verifier.verifyIssuer(claim, v, r, s);
  require(issuer != address(0));
  knows[issuer][claim.subject] = block.number;
  return true;
}

Claimのネストと埋め込み

Claimは、署名(v, r, s)を含む別の構造体に埋め込むことができます。 これにより、複雑な構造をもった証明チェーンを構築可能です。

solidity
struct VerifiableReference {
  Know delegate;
  uint8 v;
  bytes32 r;
  bytes32 s;
}

struct Introduction {
  address recipient;
  VerifiableReference issuer;
}

State Channelでの活用

ERC1812では具体的なState Channelの仕様には踏み込みませんが、EIP712に基づくプロトコルであれば、プライベートなIdentity Claimのやりとりにこの仕組みを組み込むことが可能です。 オフチェーンで身元確認情報を共有し、最終的にはブロックチェーンに投稿せずにChannelを終了できます。

キーの委任と署名(Key Delegation)

Claimの署名者が常にそのClaimの発行主体であるとは限りません。 ビジネス上の要件や運用上の都合から、署名を別のキーペアに委任したいケースがあります。 例えばサーバー側の署名ローテーションや社員ごとの発行権限管理などです。

このときは、issuerというフィールドを追加した構造体で表現できます。

solidity
struct Know {
  address subject;
  address issuer;
  uint256 validFrom;
  uint256 validTo;
}

この場合、発行主体(issuer)自身でなく、その委任先が署名しているかどうかをERC1056(EthereumDIDRegistry)を使って確認します。

solidity
registry.validDelegate(issuer, "veriKey", recoveredAddress);

ERC1056については以下の記事を参考にしてください。

埋め込み型の委任証明(Embedded Delegation Proof)

特定のClaimの発行権限を持つ委任者(Delegate)を、Claim内に証明付きで埋め込むことも可能です。

solidity
struct Delegate {
  address issuer;
  address subject;
  uint256 validFrom;
  uint256 validTo;
}

struct VerifiableDelegate {
  Delegate delegate;
  uint8 v;
  bytes32 r;
  bytes32 s;
}

struct Know {
  address subject;
  VerifiableDelegate issuer;
  uint256 validFrom;
  uint256 validTo;
}

このようにすれば、組織の内部権限管理をClaim構造に組み込めます。

Claimの種類

Binary Claim(真偽型)

例:対象アドレスが「人間である」、「所有者である」

solidity
struct Person {
  address issuer;
  address subject;
  uint256 validFrom;
  uint256 validTo;
}

Value Claim(値付き)

具体的な値を持つ主張です。 例として「名前がAlice」、「口座残高が1234555」など。

solidity
struct Name {
  address issuer;
  address subject;
  string name;
  uint256 validFrom;
  uint256 validTo;
}

ただし、スマートコントラクトで扱うとPII(個人情報)としてのリスクがあるため、パブリックブロックチェーン上では慎重な取り扱いが必要です。

Hashed Claim(ハッシュ型)

個人情報を直接書かず、ハッシュ化して証明する形式です。 例:「名前のハッシュが keccak256("Alice")」

solidity
struct Email {
  address issuer;
  address subject;
  bytes32 keccak256;
  uint256 validFrom;
  uint256 validTo;
}

EIP712 Domain構成

EIP712では署名対象のスキーマを定義する必要があります。 Domainはいかのように設定します。

json
{
  "name": "EIP1???Claim",
  "version": "1",
  "chainId": 1,
  "verifyingContract": "0x...",
  "salt": "..."
}

Claimごとに異なる型(Type)を定義し、署名付きのメッセージとして提示します。

Revocation(失効)

Claimは、発行者または対象者が失効(revoke)できるようにすべきです。 失効状態はオンチェーンのRegistryで管理され、Verifying Contractがそのステートを参照できます。

solidity
contract RevocationRegistry {
  mapping (bytes32 => mapping (address => uint)) public revocations;

  function revoke(bytes32 digest) public returns (bool) {
    revocations[digest][msg.sender] = block.number;
    return true;
  }

  function revoked(address party, bytes32 digest) public view returns (bool) {
    return revocations[digest][party] > 0;
  }
}

検証側はこのRegistryに対して失効チェックを行います。

solidity
require(!revocations.revoked(issuer, digest), "claim was revoked by issuer");
require(!revocations.revoked(claim.subject, digest), "claim was revoked by subject");

Verifiable Claims Domainの作成

Verifiable Claimsをどう分類・標準化するかはこの提案の範囲外です。 EIPやその他の標準化手法によって、重要なClaimスキーマ(KYC、投資家資格など)を策定することが期待されます。 開発者は、ドメインやClaimの粒度をユースケースに応じて柔軟に設計できます。

補足

なぜEIP712のSigned Typed Dataを使うのか

EIP712は、EthereumのLayer 1およびLayer 2におけるTyped Data(型付きデータ)署名の標準仕様です。 この仕様は、検証可能な証明(Verifiable Claims)を構築するための堅牢な基盤を提供します。

署名済みTyped Dataは、構造化されたデータに対して署名を行うため、データの整合性と署名者の正当性を確実に検証できます。 これは、KYC(本人確認)や資産保有証明、ロールベース認証といった実社会のユースケースにおいて、信頼性の高いデジタル証明を実現する鍵となります。

なぜ単一のEIP712ドメインを使わないのか

EIP712では、各署名に対応する「ドメイン(Domain)」という概念があります。 これは、署名の有効範囲や対象を明確にするための名前空間のようなもので、nameやversion、chainId、verifyingContractなどの情報を含みます。

ERC1812では、すべてのClaimを単一のEIP712ドメインで統一せず、用途ごとに異なるドメインを用いることを推奨しています。 その理由は以下の通りです。

  • Claimの目的を明示できる ドメインが異なることで、例えば「本人確認用のClaim」と「所有権証明のClaim」を混同せずに扱えます。
  • 誤用を防ぐ ドメインと型(Type)によって、特定用途に限定されたClaimを定義でき、他の目的での再利用を防げます。
  • コミュニティ主導の拡張が容易 用途ごとに異なるドメイン・型を作ることで、新しいユースケースに対する試行錯誤や標準化がしやすくなります。

つまり、用途ごとのドメインを柔軟に設計できることが、EIP712を使う大きな利点であり、Verifiable Claimsの発展にとって不可欠だという考え方です。

テストケース

ERC1812では、EIP712に基づいた検証可能な証明を利用するための具体的なSolidity実装例が提供されています。 以下に、代表的な検証器(Verifier)とスマートコントラクトの例を紹介します。

シンプルなID検証用のVerifier

このVerifierは、最小限の個人情報を含む非常にシンプルなID証明(IdVerification Claim)を検証します。 例えば、特定の個人について「本人確認が済んでいるかどうか」などの証明です。 個人情報の露出を抑えつつ、スマートコントラクト上で信頼できる確認が可能になります。

所有権証明のVerifier

ユーザーのウォレットによって署名された所有権証明(OwnershipProof)を検証するVerifierです。 ユーザーが自分のアドレスの保有を証明したい場合などに活用されます。

スマートコントラクト例:KYCCoin.sol

このスマートコントラクトは、トークン保有者に対して再利用可能なID証明(IdVerification Claim)を使ってアドレスをホワイトリストに登録する機能を持ちます。 信頼された検証者によって発行されたClaimがあるユーザーは、自分でトークンの利用許可を獲得できます。

スマートコントラクト例:ConsortiumAgreement.sol

このコントラクトは、複数の団体や企業によって形成されたコンソーシアムにおける権限管理を想定したものです。 各メンバーは、自分の社員やシステムに対して、代理としての行動を許可する「Delegated Claims(委任証明)」を発行できます。 これにより、組織内での役割の委任やアクセス制御が柔軟に行えます。

RevocationRegistry(失効レジストリ)

Claimが後から無効になった場合に備え、失効状態を管理する共有レジストリ RevocationRegistry.sol も実装されています。 発行者またはClaim対象者がClaimの失効を宣言することで、検証時にそのClaimが有効かどうかをチェックできます。

この仕組みによって、時間や状態に依存する信頼性の高い証明フレームワークが成立します。

引用

Pelle Braendgaard (@pelle), "ERC-1812: Ethereum Verifiable Claims [DRAFT]," Ethereum Improvement Proposals, no. 1812, March 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1812.

最後に

今回は「EIP712の署名機能を活用することで、個人情報をブロックチェーンに保存せずに、安全かつ再利用可能な本人確認などの証明をオフチェーンで行える仕組みを提案しているERC1812」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!