はじめに
初めまして。
CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
代表的なゲームはクリプトスペルズというブロックチェーンゲームです。
今回は、特定のアドレスのコントラクトが、特定の機能を実装しているかやどこで実装されているかの情報を取得できる仕組みを提案している規格であるERC820についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
他にも様々なERCについてまとめています。
概要
この規格は、スマートコントラクトやアカウントがどのような機能を提供しているかを一元的に登録し確認できるシステムを定義しています。
簡単に言うと、あるアドレスが特定の機能を持っているか、そしてその機能がどこで実装されているかを知りたい場合、このレジストリを調べることで答えを得られます。
具体的には、この規格により作成される「ユニバーサルレジストリコントラクト」というものがあります。
これは、任意のアドレス(スマートコントラクトや通常のアカウント)が自分がサポートしているインターフェース(機能の種類)と、そのインターフェースの実装を担うコントラクトを登録できる場所です。
例えば、スマートコントラクトがERC20やERC721などの特定の標準をサポートしているかどうかを知りたい場合、このレジストリを確認します。
最後の28
バイトが0
のインターフェースはERC165として認識され、この場合、レジストリは自動的にそのコントラクトに問い合わせて、指定されたインターフェースが実装されているかを確認します。
ERC20については以下の記事を参考にしてください。
ERC721については以下の記事を参考にしてください。
さらに、このコントラクトはERC165の結果をキャッシュすることで、問い合わせのたびにかかるガス(取引のコスト)を減らす役割も果たします。
つまり、一度確認した結果は記憶され、同じ問い合わせに対しては再び計算せずにすぐ結果を提供できるようになります。
ERC165については以下の記事を参考にしてください。
このシステムはどのブロックチェーン上でもデプロイ可能で、異なるチェーン間で同じアドレスを共有することができます。
これにより、異なるチェーンにまたがるスマートコントラクトやアカウントの機能を、シームレスに追跡しやり取りすることが可能になります。
動機
Ethereumにおける「pseudo-introspection」の定義方法にはいくつかのアプローチがありますが、それぞれに問題点がありました。
最初の方法であるERC165は通常のアカウントでは使えないという大きな制限があります。
次に試みられたERC672は、ENS(Ethereum Name Service)の逆引きを使っていますが、これは複雑すぎる上にENS自体がマルチシグによってコントロールされる中央集権的なコントラクトであるため、理論上はシステムが変更される可能性があります。
pseudo-introspection
「introspection(内省)」とは、プログラムが自身の構造やプロパティについて情報を得ることができるプロセスのことを指します。
これは、オブジェクトがどのようなメソッドや属性を持っているかをランタイムで調べることができる言語の機能で、多くの高級プログラミング言語に備わっています。
しかし、全てのプログラミング言語や環境が「introspection(内省)」をサポートしているわけではありません。
特に、スマートコントラクトのような特定のシステムでは、ランタイム時にオブジェクトについての情報を取得することは、パフォーマンスの問題やセキュリティのリスクを生む可能性があります。
ここで「pseudo-introspection(擬似内省)」が登場します。
「pseudo-introspection(擬似内省)」は、真の「introspection(内省)」が可能でない環境で、オブジェクトやコントラクトがどのようなインターフェースや機能をサポートしているかを知るための仕組みを提供します。
Ethereumのスマートコントラクトでは、例えばERC165やERC820といった規格が「pseudo-introspection(擬似内省)」のアプローチを提供しています。
これらの規格では、コントラクトが特定のインターフェースをサポートしているかどうかを外部から確認できるようにするためのメカニズムを定義しています。
「pseudo-introspection(擬似内省)」はコントラクトが自己申告方式で自身がどのような機能を持っているかを公開し、他のコントラクトやアカウントがそれを確認できるようにすることで、真の「introspection(内省)」の代わりを果たします。
これにより、システムのパフォーマンスを損なうことなく、相互運用性と柔軟性を高めることができます。
これに対して、ここで紹介されている新しい規格は、ERC672よりもずっとシンプルで、完全に分散化された形を取っています。
さらに、この規格はすべてのブロックチェーンチェーンに対してユニークなアドレスを提供し、異なるチェーンごとに正しいレジストリアドレスを見つける問題を解決します。
つまり、この規格はEthereum上のコントラクトやアカウントが持つ機能を確認する方法を、よりシンプルで効率的にし、異なるチェーンにわたっても一貫性を持たせることができます。
中央集権的なシステムに依存することなく、どのコントラクトがどの機能を持っているかを簡単に確認できるようになります。
これにより、Ethereumのエコシステム全体がより透明で分散化され、使いやすくなることが期待されます。
仕様
ERC820レジストリスマートコントラクト
This is an exact copy of the code of the ERC820 registry smart contract.
これはERC820レジストリスマートコントラクトのコードの正確なコピーです。
/* ERC820 Pseudo-introspection Registry Contract
* This standard defines a universal registry smart contract where any address
* (contract or regular account) can register which interface it supports and
* which smart contract is responsible for its implementation.
*
* Written in 2018 by Jordi Baylina and Jacques Dafflon
*
* To the extent possible under law, the author(s) have dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along
* with this software. If not, see
* <https://creativecommons.org/publicdomain/zero/1.0/>.
*
* ███████╗██████╗ ██████╗ █████╗ ██████╗ ██████╗
* ██╔════╝██╔══██╗██╔════╝██╔══██╗╚════██╗██╔═████╗
* █████╗ ██████╔╝██║ ╚█████╔╝ █████╔╝██║██╔██║
* ██╔══╝ ██╔══██╗██║ ██╔══██╗██╔═══╝ ████╔╝██║
* ███████╗██║ ██║╚██████╗╚█████╔╝███████╗╚██████╔╝
* ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝
*
* ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗
* ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
* ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝
* ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝
* ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║
* ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
*
*/
pragma solidity 0.4.24;
// IV is value needed to have a vanity address starting with `0x820`.
// IV: 9513
/// @dev The interface a contract MUST implement if it is the implementer of
/// some (other) interface for any address other than itself.
interface ERC820ImplementerInterface {
/// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.
/// @param interfaceHash keccak256 hash of the name of the interface
/// @param addr Address for which the contract will implement the interface
/// @return ERC820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
}
/// @title ERC820 Pseudo-introspection Registry Contract
/// @author Jordi Baylina and Jacques Dafflon
/// @notice This contract is the official implementation of the ERC820 Registry.
/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820
contract ERC820Registry {
/// @notice ERC165 Invalid ID.
bytes4 constant INVALID_ID = 0xffffffff;
/// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
bytes4 constant ERC165ID = 0x01ffc9a7;
/// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC820_ACCEPT_MAGIC"));
mapping (address => mapping(bytes32 => address)) interfaces;
mapping (address => address) managers;
mapping (address => mapping(bytes4 => bool)) erc165Cached;
/// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.
event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
/// @notice Indicates `newManager` is the address of the new manager for `addr`.
event ManagerChanged(address indexed addr, address indexed newManager);
/// @notice Query if an address implements an interface and through which contract.
/// @param _addr Address being queried for the implementer of an interface.
/// (If `_addr == 0` then `msg.sender` is assumed.)
/// @param _interfaceHash keccak256 hash of the name of the interface as a string.
/// E.g., `web3.utils.keccak256('ERC777Token')`.
/// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`
/// or `0x0` if `_addr` did not register an implementer for this interface.
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
address addr = _addr == 0 ? msg.sender : _addr;
if (isERC165Interface(_interfaceHash)) {
bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;
}
return interfaces[addr][_interfaceHash];
}
/// @notice Sets the contract which implements a specific interface for an address.
/// Only the manager defined for that address can set it.
/// (Each address is the manager for itself until it sets a new manager.)
/// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)
/// @param _interfaceHash keccak256 hash of the name of the interface as a string.
/// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.
/// @param _implementer Contract address implementing _interfaceHash for _addr.
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
address addr = _addr == 0 ? msg.sender : _addr;
require(getManager(addr) == msg.sender, "Not the manager");
require(!isERC165Interface(_interfaceHash), "Must not be a ERC165 hash");
if (_implementer != 0 && _implementer != msg.sender) {
require(
ERC820ImplementerInterface(_implementer)
.canImplementInterfaceForAddress(_interfaceHash, addr) == ERC820_ACCEPT_MAGIC,
"Does not implement the interface"
);
}
interfaces[addr][_interfaceHash] = _implementer;
emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
}
/// @notice Sets the `_newManager` as manager for the `_addr` address.
/// The new manager will be able to call `setInterfaceImplementer` for `_addr`.
/// @param _addr Address for which to set the new manager.
/// @param _newManager Address of the new manager for `addr`.
function setManager(address _addr, address _newManager) external {
require(getManager(_addr) == msg.sender, "Not the manager");
managers[_addr] = _newManager == _addr ? 0 : _newManager;
emit ManagerChanged(_addr, _newManager);
}
/// @notice Get the manager of an address.
/// @param _addr Address for which to return the manager.
/// @return Address of the manager for a given address.
function getManager(address _addr) public view returns(address) {
// By default the manager of an address is the same address
if (managers[_addr] == 0) {
return _addr;
} else {
return managers[_addr];
}
}
/// @notice Compute the keccak256 hash of an interface given its name.
/// @param _interfaceName Name of the interface.
/// @return The keccak256 hash of an interface name.
function interfaceHash(string _interfaceName) external pure returns(bytes32) {
return keccak256(abi.encodePacked(_interfaceName));
}
/* --- ERC165 Related Functions --- */
/* --- Developed in collaboration with William Entriken. --- */
/// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
/// @param _contract Address of the contract for which to update the cache.
/// @param _interfaceId ERC165 interface for which to update the cache.
function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;
erc165Cached[_contract][_interfaceId] = true;
}
/// @notice Checks whether a contract implements an ERC165 interface or not.
/// The result may be cached, if not a direct lookup is performed.
/// @param _contract Address of the contract to check.
/// @param _interfaceId ERC165 interface to check.
/// @return `true` if `_contract` implements `_interfaceId`, false otherwise.
function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
if (!erc165Cached[_contract][_interfaceId]) {
return implementsERC165InterfaceNoCache(_contract, _interfaceId);
}
return interfaces[_contract][_interfaceId] == _contract;
}
/// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
/// @param _contract Address of the contract to check.
/// @param _interfaceId ERC165 interface to check.
/// @return `true` if `_contract` implements `_interfaceId`, false otherwise.
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
uint256 success;
uint256 result;
(success, result) = noThrowCall(_contract, ERC165ID);
if (success == 0 || result == 0) {
return false;
}
(success, result) = noThrowCall(_contract, INVALID_ID);
if (success == 0 || result != 0) {
return false;
}
(success, result) = noThrowCall(_contract, _interfaceId);
if (success == 1 && result == 1) {
return true;
}
return false;
}
/// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
/// @param _interfaceHash The hash to check.
/// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.
function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
}
/// @dev Make a call on a contract without throwing if the function does not exist.
function noThrowCall(address _contract, bytes4 _interfaceId)
internal view returns (uint256 success, uint256 result)
{
bytes4 erc165ID = ERC165ID;
assembly {
let x := mload(0x40) // Find empty storage location using "free memory pointer"
mstore(x, erc165ID) // Place signature at beginning of empty storage
mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
success := staticcall(
30000, // 30k gas
_contract, // To addr
x, // Inputs are stored at location x
0x08, // Inputs are 8 bytes long
x, // Store output over input (saves space)
0x20 // Outputs are 32 bytes long
)
result := mload(x) // Load the result
}
}
}
変数
INVALID_ID
bytes4 constant INVALID_ID = 0xffffffff;
概要
有効でないERC165のID。
詳細
この定数は、ERC165インターフェース識別子が無効であることを示すために使用されます。
ERC165は、コントラクトが特定のインターフェースをサポートしているかどうかを検出するための標準です。
ERC165ID
bytes4 constant ERC165ID = 0x01ffc9a7;
概要
ERC165のsupportsInterface
メソッドのメソッドID。
詳細
この定数は、コントラクトがsupportsInterface
関数を持っているかどうかを確認する時に使用されるERC165の固有の識別子です。
この関数は、コントラクトが特定のインターフェースをサポートしているかどうかを確認するために使用されます。
ERC820_ACCEPT_MAGIC
bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC820_ACCEPT_MAGIC"));
概要
特定のアドレスに代わってインターフェースを実装するコントラクトが存在する場合に返される値。
詳細
この定数は、ERC820規格で定義されたcanImplementInterfaceForAddress
関数の戻り値として使用されます。
特定のアドレスに代わってインターフェースを実装するコントラクトが存在する場合に、この特別な値を返すことで、その実装が存在することを確認できます。
interfaces
mapping (address => mapping(bytes32 => address)) interfaces;
概要
アドレスとインターフェースハッシュを対応付けるマッピング配列。
詳細
このマッピングを使用して、特定のアドレスがどのインターフェースを実装しているか、そしてその実装がどのコントラクトによって行われているかを追跡します。
キーはアドレスとインターフェースのハッシュで、値はそのインターフェースを実装するコントラクトのアドレスです。
パラメータ
-
address
- インターフェース実装を照会するアドレス。
-
bytes32
- インターフェースのハッシュ。
managers
mapping (address => address) managers;
概要
各アドレスのマネージャーを追跡するためのマッピングです。
詳細
各アドレスは、そのインターフェースの実装を管理するマネージャーを持つことができます。
このマッピング配列を使用して、特定のアドレスのマネージャーが誰であるかを確認できます。
パラメータ
-
address
- マネージャーを確認したいアドレス。
erc165Cached
mapping (address => mapping(bytes4 => bool)) erc165Cached;
概要
ERC165インターフェースクエリの結果をキャッシュするマッピング配列。
詳細
このキャッシュは、コントラクトが特定のERC165インターフェースを実装しているかどうかのクエリを高速化するために使用されます
。キャッシュは手動で更新される必要があります。
パラメータ
-
address
- インターフェース実装を照会するコントラクトのアドレス。
-
bytes4
- ERC165インターフェースID。
InterfaceImplementerSet
event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
概要
インターフェースの実装者が設定されたときに発行されるイベント。
詳細
このイベントは、特定のアドレスに対する特定のインターフェースの実装者が設定または変更されたときに発行されます。
これにより、インターフェースの実装がどのコントラクトによって行われているかを外部から追跡することが可能になります。
パラメータ
-
addr
- インターフェースの実装者が設定されたアドレス。
-
interfaceHash
- インターフェースのハッシュ。
-
implementer
- インターフェースを実装するコントラクトのアドレス。
ManagerChanged
event ManagerChanged(address indexed addr, address indexed newManager);
概要
アドレスのマネージャーが変更されたときに発行されるイベント。
詳細
特定のアドレスのマネージャーが新しいアドレスに変更されたときに発行されます。
これにより、マネージャーの変更を外部から追跡することが可能になります。
パラメータ
-
addr
- マネージャーが変更されたアドレス。
-
newManager
- 新しいマネージャーのアドレス。
関数
getInterfaceImplementer
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address)
概要
アドレスが特定のインターフェースを実装しているか、そしてその実装がどのコントラクトを通じて行われているか照会する関数。
詳細
もし_addr
が0
の場合、msg.sender
が想定されます。
もし_interfaceHash
がERC165インターフェースである場合、該当するコントラクトに問い合わせを行い、インターフェースが実装されているかを確認します。
それ以外の場合、interfaces
マッピングを使用して実装コントラクトのアドレスを返します。
引数
-
_addr
- インターフェースの実装者を照会するアドレス。
-
_interfaceHash
- インターフェース名のkeccak256ハッシュ。
戻り値
- アドレス
-
_interfaceHash
で指定されたインターフェースを実装しているコントラクトのアドレス。 - 実装者が登録されていない場合は
0x0
。
-
setInterfaceImplementer
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external
概要
特定のアドレスに対して特定のインターフェースを実装するコントラクトを設定する関数。
詳細
_addr
が0
の場合、msg.sender
が想定されます。
この関数を呼び出せるのはそのアドレスのマネージャーのみです。
_interfaceHash
がERC165インターフェースでないこと、そして_implementer
が指定されたインターフェースを実際に実装していることが要求されます。
引数
-
_addr
- インターフェースを定義するアドレス。
-
_interfaceHash
- インターフェース名のkeccak256ハッシュ。
-
_implementer
-
_interfaceHash
を実装するコントラクトのアドレス。
-
setManager
function setManager(address _addr, address _newManager) external
概要
特定のアドレスに新しいマネージャーを設定する関数。
詳細
この関数を呼び出せるのは現在のマネージャーのみです。
_newManager
が0x0
の場合、マネージャーは_addr
自身にリセットされます。
引数
-
_addr
- 新しいマネージャーを設定するアドレス。
-
_newManager
- 新しいマネージャーのアドレス。
getManager
function getManager(address _addr) public view returns(address)
概要
特定のアドレスのマネージャーを取得する関数。
詳細
デフォルトでは、アドレスのマネージャーはそのアドレス自身です。
マネージャーが設定されていない場合は、アドレス自身が返されます。
引数
-
_addr
- マネージャーを返すアドレス。
戻り値
- アドレス
- 指定されたアドレスのマネージャーのアドレス。
interfaceHash
function interfaceHash(string _interfaceName) external pure returns(bytes32)
概要
与えられたインターフェース名からkeccak256ハッシュを計算する関数。
詳細
この関数は、文字列として与えられたインターフェース名のkeccak256ハッシュを返します。
このハッシュは、インターフェースの一意の識別子として機能します。
引数
-
_interfaceName
- インターフェースの名前。
戻り値
-
bytes
- インターフェース名のkeccak256ハッシュ。
デプロイトランザクション
0xf90a2a8085174876e800830c35008080b909d7608060405234801561001057600080fd5b506109b7806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf5780635df8122f146100fc57806365ba36c114610123578063a41e7d5114610155578063aabbb8ca14610183578063b7056765146101a7578063f712f3e8146101e9575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610217565b005b3480156100cb57600080fd5b506100e0600160a060020a0360043516610512565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a036004358116906024351661055e565b34801561012f57600080fd5b506101436004803560248101910135610655565b60408051918252519081900360200190f35b34801561016157600080fd5b506100bd600160a060020a0360043516600160e060020a0319602435166106e3565b34801561018f57600080fd5b506100e0600160a060020a036004351660243561076d565b3480156101b357600080fd5b506101d5600160a060020a0360043516600160e060020a0319602435166107e7565b604080519115158252519081900360200190f35b3480156101f557600080fd5b506101d5600160a060020a0360043516600160e060020a03196024351661089c565b6000600160a060020a0384161561022e5783610230565b335b90503361023c82610512565b600160a060020a03161461029a576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6102a38361091c565b156102f8576040805160e560020a62461bcd02815260206004820152601960248201527f4d757374206e6f74206265206120455243313635206861736800000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103195750600160a060020a0382163314155b156104a15760405160200180807f4552433832305f4143434550545f4d414749430000000000000000000000000081525060130190506040516020818303038152906040526040518082805190602001908083835b6020831061038d5780518252601f19909201916020918201910161036e565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207f249cb3fa000000000000000000000000000000000000000000000000000000008352600483018a9052600160a060020a0388811660248501529451909650938816945063249cb3fa936044808401945091929091908290030181600087803b15801561042057600080fd5b505af1158015610434573d6000803e3d6000fd5b505050506040513d602081101561044a57600080fd5b5051146104a1576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03808216600090815260016020526040812054909116151561053c575080610559565b50600160a060020a03808216600090815260016020526040902054165b919050565b3361056883610512565b600160a060020a0316146105c6576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146105e557806105e8565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b60008282604051602001808383808284378201915050925050506040516020818303038152906040526040518082805190602001908083835b602083106106ad5780518252601f19909201916020918201910161068e565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902090505b92915050565b6106ed82826107e7565b6106f85760006106fa565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b60008080600160a060020a038516156107865784610788565b335b91506107938461091c565b156107b85750826107a4828261089c565b6107af5760006107b1565b815b92506107df565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b60008080610815857f01ffc9a70000000000000000000000000000000000000000000000000000000061093e565b9092509050811580610825575080155b1561083357600092506107df565b61084585600160e060020a031961093e565b909250905081158061085657508015155b1561086457600092506107df565b61086e858561093e565b90925090506001821480156108835750806001145b1561089157600192506107df565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108e4576108dd83836107e7565b90506106dd565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a723058204fc4461c9d5a247b0eafe0f9c508057bc0ad72bc24668cb2a35ea65850e10d3100291ba08208208208208208208208208208208208208208208208208208208208208200a00820820820820820820820820820820820820820820820820820820820820820
上記のデータは、ERC820という特定の機能を持つスマートコントラクトをブロックチェーンに設置するためのデータです。
このデータは、コントラクトをEthereumブロックチェーンなどの任意のチェーン上に「デプロイ」する時に使われます。
トランザクションデータの最後にある「820
」という数字は、署名の一部を表しています。
この署名はトランザクションが正当なものであることを証明するために重要です。
署名にある特定のパターンは、そのコントラクトが安全にデプロイされたことを示し、使用されたアカウントの秘密鍵が公開されていないことを意味します。
このテキストにあるデータは、スマートコントラクトをブロックチェーンに安全にデプロイするための指示です。
署名の特定のパターンが含まれていることで、コントラクトのデプロイが信頼できる方法で行われたことが保証されます。
これにより、コントラクトが想定通りに機能し、意図しない操作が行われないようにすることができます。
デプロイメソッド
「キーレスデプロイメント方法」は、スマートコントラクトをブロックチェーンにデプロイする時に使われる特別な方法で、ニックの方法とも呼ばれます。
この方法では、一度だけ使用されるアカウント(一時的なアドレス)からコントラクトをデプロイします。
手順は次のようになります。
1. ランダムアカウント生成
まず、新しくランダムなアカウントからコントラクトをデプロイするためのトランザクションを生成します。
このとき、EIP155というプロトコルは使わず、どのブロックチェーンでも動作するようにします。
ERC155については以下の記事を参考にしてください。
2. ガス価格設定
トランザクションは比較的高いガス価格(この場合100 Gwei
)で設定され、どのチェーンでもデプロイできるようにします。
3. 署名設定
トランザクションの署名部分にあるv
, r
, s
という値を特定の数値に設定します。
ここで、r
とs
は**'820'**の繰り返しパターンを持つ予測可能な値です。
これらは、人間によって特別に作られた「ランダムな数値」のように機能します。
v: 27
r: 0x8208208208208208208208208208208208208208208208208208208208208200
s: 0x0820820820820820820820820820820820820820820820820820820820820820
The values of r and s must be 32 bytes long each—or 64 characters in hexadecimal. Since 820 is 3 characters long and 3 is not a divisor of 64, but it is a divisor of 63, the r and s values are padded with one extra character.
The s value is prefixed with a single zero (0). The 0 prefix also guarantees that s < secp256k1n ÷ 2 + 1.
The r value, cannot be prefixed with a zero, as the transaction becomes invalid. Instead it is suffixed with a zero (0) which still respects the condition s < secp256k1n.
rとsの値はそれぞれ32バイト、または16進数で64文字の長さでなければなりません。'820'は3文字の長さであり、3は64の約数ではありませんが63の約数ですので、rとsの値は1文字余分にパディングされます。
sの値の先頭には単一のゼロ(0)が付けられます。このゼロの接頭辞はs < secp256k1n ÷ 2 + 1という条件を保証するのにも役立ちます。
一方、rの値にはゼロを接頭辞として付けることはできません。これをするとトランザクションが無効になるためです。代わりにゼロ(0)が接尾辞として付けられ、それでもs < secp256k1nという条件を尊重します。
4. 値の長さ調整
r
とs
はそれぞれ32
バイト長になるよう調整され、**'820'**のパターンで満たされます。
ただし、64
文字の要件を満たすために、1文字分の余分なパディングが加えられます。
5. 接頭辞と接尾辞
s
の値には0
を接頭辞として、r
の値には0
を接尾辞として加えます。
これにより、特定の暗号学的条件を満たし、トランザクションが有効であることを保証します。
このプロセスを経て、トランザクションの送信者、つまり一回限りのデプロイメントアカウントが確定されます。
このアカウントからトランザクションを発信できますが、その秘密鍵は誰にも知られていないという保証があります。
Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account.
これにより、そのトランザクションをブロードキャストできるアカウントを得ることができますが、そのアカウントの秘密鍵を誰も知らないという保証も同時に得られます。
次に、この1回限り使用するアカウントに0.08 ETH
を送金します。
最後に、デプロイメントトランザクションを全てのチェーン上でブロードキャスト(公開)します。
この方法により、コントラクトアドレスがどのチェーン上でも常に同じになり、そのアドレスが異なるコントラクトに使用されることはないという保証が得られます。
単一レジストリデプロイアカウント
0xE6C244a1C10Aa0085b0cf92f04cdaD947C2988b8
このアドレスは、スマートコントラクトをブロックチェーンにデプロイするためだけに一度だけ使われる特別なアカウントです。
このアカウントは、署名データから逆算することで作成されていて、その秘密鍵は誰にも分かりませんが、コントラクトをブロックチェーンに設置するトランザクションを正式に行う権限があることは確認されています。
この特別なアカウントを使ってコントラクトをブロックチェーンにデプロイするには、まずこのアカウントに0.08 ETH
(ブロックチェーン上での取引を行うための通貨)を送金する必要があります。
この送金によって、アカウントにはコントラクトをブロックチェーンにデプロイするためのトランザクションを行うのに十分な資金が提供されます。
そして、この一度きりのアカウントを通じてコントラクトがブロックチェーンに正式に設置されるわけです。
To deploy the registry, 0.08 ethers MUST be sent to this account first.
レジストリをデプロイするには、まずこのアカウントに0.08 ETH
を送らなければならない。
レジストリコントラクトアドレス
0x820b586C8C28125366C998641B09DCbE7d4cBF06
レジストリコントラクトは、そのコントラクトがデプロイされているすべてのチェーンに対して上記のアドレスを持ちます。
ERC820Registryコントラクトのメタデータ
{ "compiler": { "version": "0.4.24+commit.e67f0147" }, "language": "Solidity", "output": { "abi": [ { "constant": false, "inputs": [ { "name": "_addr", "type": "address" }, { "name": "_interfaceHash", "type": "bytes32" }, { "name": "_implementer", "type": "address" } ], "name": "setInterfaceImplementer", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_addr", "type": "address" } ], "name": "getManager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_addr", "type": "address" }, { "name": "_newManager", "type": "address" } ], "name": "setManager", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_interfaceName", "type": "string" } ], "name": "interfaceHash", "outputs": [ { "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "pure", "type": "function" }, { "constant": false, "inputs": [ { "name": "_contract", "type": "address" }, { "name": "_interfaceId", "type": "bytes4" } ], "name": "updateERC165Cache", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_addr", "type": "address" }, { "name": "_interfaceHash", "type": "bytes32" } ], "name": "getInterfaceImplementer", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "_contract", "type": "address" }, { "name": "_interfaceId", "type": "bytes4" } ], "name": "implementsERC165InterfaceNoCache", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "_contract", "type": "address" }, { "name": "_interfaceId", "type": "bytes4" } ], "name": "implementsERC165Interface", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "addr", "type": "address" }, { "indexed": true, "name": "interfaceHash", "type": "bytes32" }, { "indexed": true, "name": "implementer", "type": "address" } ], "name": "InterfaceImplementerSet", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "addr", "type": "address" }, { "indexed": true, "name": "newManager", "type": "address" } ], "name": "ManagerChanged", "type": "event" } ], "devdoc": { "author": "Jordi Baylina and Jacques Dafflon", "methods": { "getInterfaceImplementer(address,bytes32)": { "params": { "_addr": "Address being queried for the implementer of an interface. (If `_addr == 0` then `msg.sender` is assumed.)", "_interfaceHash": "keccak256 hash of the name of the interface as a string. E.g., `web3.utils.keccak256('ERC777Token')`." }, "return": "The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0x0` if `_addr` did not register an implementer for this interface." }, "getManager(address)": { "params": { "_addr": "Address for which to return the manager." }, "return": "Address of the manager for a given address." }, "implementsERC165Interface(address,bytes4)": { "params": { "_contract": "Address of the contract to check.", "_interfaceId": "ERC165 interface to check." }, "return": "`true` if `_contract` implements `_interfaceId`, false otherwise." }, "implementsERC165InterfaceNoCache(address,bytes4)": { "params": { "_contract": "Address of the contract to check.", "_interfaceId": "ERC165 interface to check." }, "return": "`true` if `_contract` implements `_interfaceId`, false otherwise." }, "interfaceHash(string)": { "params": { "_interfaceName": "Name of the interface." }, "return": "The keccak256 hash of an interface name." }, "setInterfaceImplementer(address,bytes32,address)": { "params": { "_addr": "Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)", "_implementer": "Contract address implementing _interfaceHash for _addr.", "_interfaceHash": "keccak256 hash of the name of the interface as a string. For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface." } }, "setManager(address,address)": { "params": { "_addr": "Address for which to set the new manager.", "_newManager": "Address of the new manager for `addr`." } }, "updateERC165Cache(address,bytes4)": { "params": { "_contract": "Address of the contract for which to update the cache.", "_interfaceId": "ERC165 interface for which to update the cache." } } }, "title": "ERC820 Pseudo-introspection Registry Contract" }, "userdoc": { "methods": { "getInterfaceImplementer(address,bytes32)": { "notice": "Query if an address implements an interface and through which contract." }, "getManager(address)": { "notice": "Get the manager of an address." }, "implementsERC165Interface(address,bytes4)": { "notice": "Checks whether a contract implements an ERC165 interface or not. The result may be cached, if not a direct lookup is performed." }, "implementsERC165InterfaceNoCache(address,bytes4)": { "notice": "Checks whether a contract implements an ERC165 interface or not without using nor updating the cache." }, "interfaceHash(string)": { "notice": "Compute the keccak256 hash of an interface given its name." }, "setInterfaceImplementer(address,bytes32,address)": { "notice": "Sets the contract which implements a specific interface for an address. Only the manager defined for that address can set it. (Each address is the manager for itself until it sets a new manager.)" }, "setManager(address,address)": { "notice": "Sets the `_newManager` as manager for the `_addr` address. The new manager will be able to call `setInterfaceImplementer` for `_addr`." }, "updateERC165Cache(address,bytes4)": { "notice": "Updates the cache with whether the contract implements an ERC165 interface or not." } } } }, "settings": { "compilationTarget": { "./contracts/ERC820Registry.sol": "ERC820Registry" }, "evmVersion": "byzantium", "libraries": {}, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [] }, "sources": { "./contracts/ERC820Registry.sol": { "content": "/* ERC820 Pseudo-introspection Registry Contract\n * This standard defines a universal registry smart contract where any address\n * (contract or regular account) can register which interface it supports and\n * which smart contract is responsible for its implementation.\n *\n * Written in 2018 by Jordi Baylina and Jacques Dafflon\n *\n * To the extent possible under law, the author(s) have dedicated all copyright\n * and related and neighboring rights to this software to the public domain\n * worldwide. This software is distributed without any warranty.\n *\n * You should have received a copy of the CC0 Public Domain Dedication along\n * with this software. If not, see\n * <https://creativecommons.org/publicdomain/zero/1.0/>.\n *\n * ███████╗██████╗ ██████╗ █████╗ ██████╗ ██████╗\n * ██╔════╝██╔══██╗██╔════╝██╔══██╗╚════██╗██╔═████╗\n * █████╗ ██████╔╝██║ ╚█████╔╝ █████╔╝██║██╔██║\n * ██╔══╝ ██╔══██╗██║ ██╔══██╗██╔═══╝ ████╔╝██║\n * ███████╗██║ ██║╚██████╗╚█████╔╝███████╗╚██████╔╝\n * ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝\n *\n * ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗\n * ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝\n * ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝\n * ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝\n * ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║\n * ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝\n *\n */\npragma solidity 0.4.24;\n// IV is value needed to have a vanity address starting with `0x820`.\n// IV: 9513\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some (other) interface for any address other than itself.\ninterface ERC820ImplementerInterface {\n /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.\n /// @param interfaceHash keccak256 hash of the name of the interface\n /// @param addr Address for which the contract will implement the interface\n /// @return ERC820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.\n function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);\n}\n\n\n/// @title ERC820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820\ncontract ERC820Registry {\n /// @notice ERC165 Invalid ID.\n bytes4 constant INVALID_ID = 0xffffffff;\n /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n bytes4 constant ERC165ID = 0x01ffc9a7;\n /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.\n bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC820_ACCEPT_MAGIC\"));\n\n mapping (address => mapping(bytes32 => address)) interfaces;\n mapping (address => address) managers;\n mapping (address => mapping(bytes4 => bool)) erc165Cached;\n\n /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.\n event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n /// @notice Indicates `newManager` is the address of the new manager for `addr`.\n event ManagerChanged(address indexed addr, address indexed newManager);\n\n /// @notice Query if an address implements an interface and through which contract.\n /// @param _addr Address being queried for the implementer of an interface.\n /// (If `_addr == 0` then `msg.sender` is assumed.)\n /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n /// E.g., `web3.utils.keccak256('ERC777Token')`.\n /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`\n /// or `0x0` if `_addr` did not register an implementer for this interface.\n function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n address addr = _addr == 0 ? msg.sender : _addr;\n if (isERC165Interface(_interfaceHash)) {\n bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;\n }\n return interfaces[addr][_interfaceHash];\n }\n\n /// @notice Sets the contract which implements a specific interface for an address.\n /// Only the manager defined for that address can set it.\n /// (Each address is the manager for itself until it sets a new manager.)\n /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)\n /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.\n /// @param _implementer Contract address implementing _interfaceHash for _addr.\n function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n address addr = _addr == 0 ? msg.sender : _addr;\n require(getManager(addr) == msg.sender, \"Not the manager\");\n\n require(!isERC165Interface(_interfaceHash), \"Must not be a ERC165 hash\");\n if (_implementer != 0 && _implementer != msg.sender) {\n require(\n ERC820ImplementerInterface(_implementer)\n .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC820_ACCEPT_MAGIC,\n \"Does not implement the interface\"\n );\n }\n interfaces[addr][_interfaceHash] = _implementer;\n emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n }\n\n /// @notice Sets the `_newManager` as manager for the `_addr` address.\n /// The new manager will be able to call `setInterfaceImplementer` for `_addr`.\n /// @param _addr Address for which to set the new manager.\n /// @param _newManager Address of the new manager for `addr`.\n function setManager(address _addr, address _newManager) external {\n require(getManager(_addr) == msg.sender, \"Not the manager\");\n managers[_addr] = _newManager == _addr ? 0 : _newManager;\n emit ManagerChanged(_addr, _newManager);\n }\n\n /// @notice Get the manager of an address.\n /// @param _addr Address for which to return the manager.\n /// @return Address of the manager for a given address.\n function getManager(address _addr) public view returns(address) {\n // By default the manager of an address is the same address\n if (managers[_addr] == 0) {\n return _addr;\n } else {\n return managers[_addr];\n }\n }\n\n /// @notice Compute the keccak256 hash of an interface given its name.\n /// @param _interfaceName Name of the interface.\n /// @return The keccak256 hash of an interface name.\n function interfaceHash(string _interfaceName) external pure returns(bytes32) {\n return keccak256(abi.encodePacked(_interfaceName));\n }\n\n /* --- ERC165 Related Functions --- */\n /* --- Developed in collaboration with William Entriken. --- */\n\n /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n /// @param _contract Address of the contract for which to update the cache.\n /// @param _interfaceId ERC165 interface for which to update the cache.\n function updateERC165Cache(address _contract, bytes4 _interfaceId) external {\n interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;\n erc165Cached[_contract][_interfaceId] = true;\n }\n\n /// @notice Checks whether a contract implements an ERC165 interface or not.\n /// The result may be cached, if not a direct lookup is performed.\n /// @param _contract Address of the contract to check.\n /// @param _interfaceId ERC165 interface to check.\n /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n if (!erc165Cached[_contract][_interfaceId]) {\n return implementsERC165InterfaceNoCache(_contract, _interfaceId);\n }\n return interfaces[_contract][_interfaceId] == _contract;\n }\n\n /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n /// @param _contract Address of the contract to check.\n /// @param _interfaceId ERC165 interface to check.\n /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n uint256 success;\n uint256 result;\n\n (success, result) = noThrowCall(_contract, ERC165ID);\n if (success == 0 || result == 0) {\n return false;\n }\n\n (success, result) = noThrowCall(_contract, INVALID_ID);\n if (success == 0 || result != 0) {\n return false;\n }\n\n (success, result) = noThrowCall(_contract, _interfaceId);\n if (success == 1 && result == 1) {\n return true;\n }\n return false;\n }\n\n /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n /// @param _interfaceHash The hash to check.\n /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.\n function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n }\n\n /// @dev Make a call on a contract without throwing if the function does not exist.\n function noThrowCall(address _contract, bytes4 _interfaceId)\n internal view returns (uint256 success, uint256 result)\n {\n bytes4 erc165ID = ERC165ID;\n\n assembly {\n let x := mload(0x40) // Find empty storage location using \"free memory pointer\"\n mstore(x, erc165ID) // Place signature at beginning of empty storage\n mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n success := staticcall(\n 30000, // 30k gas\n _contract, // To addr\n x, // Inputs are stored at location x\n 0x08, // Inputs are 8 bytes long\n x, // Store output over input (saves space)\n 0x20 // Outputs are 32 bytes long\n )\n\n result := mload(x) // Load the result\n }\n }\n}\n", "keccak256": "0x8eecce3912a15087b3f5845d5a74af7712c93d0a8fcd6f2d40f07ed5032022ab" } }, "version": 1 }
インターフェース名
インターフェース名を使ってスマートコントラクトがどの機能を持っているかを特定するために、keccak256という暗号化技術を使ってその名前をユニークなハッシュ値に変換し、getInterfaceImplementer()
関数に送ります。
もしインターフェースが何かしらの標準規格に属している場合は、そのインターフェース名をはっきりと示し、公開されているERC820へのリンクを提供することが推奨されます。
これにより、他の開発者がこれらのルールを探すために追加の手間をかけることなく、必要な情報にアクセスできるようになります。
さらに、レジストリではオンチェーンで直接インターフェース名のハッシュを計算する便利な関数interfaceHash(string _interfaceName)
を提供しています。
function interfaceHash(string _interfaceName) public pure returns(bytes32)
この関数にインターフェースの名前を入力すると、その名前に対応するkeccak256ハッシュ値が出力されます。
このハッシュ値は、レジストリ内で特定のインターフェース実装を照会するために使用されます。
つまり、この機能を使用することで、開発者は簡単に特定のインターフェースがどのコントラクトに実装されているかを確認できるようになります。
identifier: 65ba36c1
parameters
_interfaceName: Name of the interface.
returns: The keccak256 hash of an interface name.
承認されたERC
インターフェースがEthereumの公式な規格であるERC(Ethereum Request for Comments)の一部である場合、その名前は特定のフォーマット「ERC###XXXXX」に従って命名する必要があります。
ここで「###」はERCの番号を示し、「XXXXX」はそのインターフェースの名前をキャメルケースで表します。
この名前付けの規則は、どのインターフェースがどのERC規格に属しているかを明確にするためです。
また、そのインターフェースの意味や使用方法は、それぞれのERC文書内で定義されます。
たとえば、「ERC20Token」、「ERC777Token」、「ERC777TokensSender」、「ERC777TokensRecipient」といったインターフェース名があります。
これらの名前は、ERC20やERC777といった特定のERC規格に関連づけられています。
システムがこれらのインターフェースを正確に識別できるように、keccak256という関数を使用して各インターフェース名からユニークなハッシュ値を生成します。
このハッシュ値は、その後、レジストリで対応するインターフェース実装を検索する際に使用されます。
ERC777については以下の記事を参考にしてください。
ERC-165対応インターフェイス
ERC165との互換性は、ウィリアム・エントリケンと共に設計・開発され、ERC165キャッシュを含んでいます。
最後の28
バイトがゼロ(0
)である任意のインターフェースは、ERC165インターフェースとみなされるべきです。
ERC165の照会
誰でもレジストリを使用して、コントラクトがERC165インターフェースを実装しているかどうかを明示的に確認できます。
そのために以下の2つの関数のどちらかを呼び出すことができます。
function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool)
コントラクトがERC165インターフェースを実装しているかどうかをチェックします。
結果はキャッシュされます。
キャッシュが古い場合は、updateERC165Cache
を呼び出して更新する必要があります(詳細はERC165キャッシュを参照)。
identifier: f712f3e8
parameters
_contract: Address of the contract to check.
_interfaceId: ERC-165 interface to check.
returns: true if _contract implements _interfaceId, false otherwise.
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool)
キャッシュを使用せずにコントラクトがERC165インターフェースを実装しているかどうかをチェックします。
identifier: b7056765
parameters
_contract: Address of the contract to check.
_interfaceId: ERC-165 interface to check.
returns: true if _contract implements _interfaceId, false otherwise.
ERC165キャッシュ
コントラクトがERC165インターフェースを実装しているかどうかは、ガスを節約するために手動でキャッシュできます。
もしコントラクトが動的にそのインターフェースを変更し、ERC820レジストリのERC165キャッシュに依存している場合、キャッシュは手動で更新されなければなりません。
キャッシュの無効化や自動更新は行われません。
理想的には、コントラクトはインターフェースを変更するときに自動的にキャッシュを更新するべきですが、誰でもコントラクトの代わりにキャッシュを更新することができます。
キャッシュの更新はupdateERC165Cache
関数を使って行わなければなりません。
function updateERC165Cache(address _contract, bytes4 _interfaceId) public
キャッシュを更新するコントラクトのアドレスと、更新するERC-165インターフェースを指定します。
identifier: a41e7d51
parameters
_contract: Address of the contract for which to update the cache.
_interfaceId: ERC-165 interface for which to update the cache.
プライベートユーザー定義インターフェイス
このシステムは自由に拡張できるように設計されています。
つまり、あなたは独自のインターフェース名を考案し、そのインターフェースを他の人が実装するよう促すことができます。
その後、あなたはその実装が存在するかどうかをチェックできます。
このプロセスを通じて、新しいアイデアや機能を楽しく開発することができます。
ただし、既に予約されている指定(例えば公式のERC20やERC721などの既存の規格)と衝突しないように注意する必要があります。
独自のインターフェースを作成する時には、既存の規格や命名規則を尊重し、それらと混同や衝突を引き起こさないようにするべきです。
アドレスにインターフェースを設定する
ERC820レジストリを使って、あるアドレスが特定のインターフェースを実装しているコントラクトを指定する方法について説明します。
この設定を行うためには、setInterfaceImplementer
という関数を使用します。
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) public
具体的には、あるアドレスがどのコントラクトによって特定のインターフェースが実装されているかを指定します。
この関数を使用するには、そのアドレスの管理者である必要があります。
基本的には、各アドレスは自分自身を管理しますが、詳細はマネージャーセクションで確認できます。
注意点として、もしインターフェースを設定するアドレスと実装コントラクトが異なる場合、実装コントラクトはERC820ImplementerInterface
を実装している必要があります。
そして、そのコントラクトに対してcanImplementInterfaceForAddress
関数を呼び出すと、ERC820_ACCEPT_MAGIC
という特定の値を返さなければなりません。
また、指定するインターフェースのハッシュ値(_interfaceHash
)は、ERC165規格のインターフェースとは異なるものでなければなりません。
つまり、最後の28
バイトが0
で終わるような値であってはいけません。
さらに、_addr
パラメータが0
の場合は、関数を呼び出したユーザー(msg.sender
)がアドレスとして想定されます。
これは、マルチシグのような複数署名が必要な場合にトランザクションデータを一定に保つための便利な機能です。
この関数を使うことで、開発者はブロックチェーン上の任意のアドレスが特定のインターフェースをどのコントラクトで実装しているかを指定し、確認することができます。
identifier: 29965a1d
parameters
_addr: Address to define the interface for (if _addr == 0 them msg.sender: is assumed)
_interfaceHash: keccak256 hash of the name of the interface as a string, for example web3.utils.keccak256('ERC777TokensRecipient') for the ERC777TokensRecipient interface.
_implementer: Contract implementing _interfaceHash for _addr.
アドレスに対するインターフェースの実装を取得
ERC820レジストリを使えば、誰でも特定のアドレスがどのコントラクトを通じてどのインターフェースを実装しているかを調べることができます。
これはgetInterfaceImplementer
関数を用いて行われます。
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) public view returns (address)
この関数は、調べたいアドレスとインターフェースのハッシュ値を入力として受け取り、そのアドレスが指定されたインターフェースを実装しているコントラクトのアドレスを返します。
もしインターフェースのハッシュ値の最後の28
バイトが0
である場合、これはERC165インターフェースとして認識されます。
その場合、レジストリは指定されたアドレスのコントラクトに問い合わせを行い、そのコントラクトがERC165インターフェースを実装しているかどうかを確認します。
さらに、ERC165に関する照会結果はキャッシュされ、ガス消費を削減します。
このキャッシュは、erc165UpdateCache
関数を通じて更新することができます。
また、照会するアドレス(_addr
)が0の場合、関数を呼び出したユーザー(msg.sender
)が対象アドレスとして想定されます。
これは、複数の署名が必要な場合などにやり取りを簡単にするための設計です。
具体的には、インターフェース名(例:ERC777Token)からそのkeccak256ハッシュ値を計算し、それと調べたいアドレスを関数に入力します。
関数はそのアドレスが指定されたインターフェースを実装しているコントラクトのアドレスを返します。もし実装が登録されていない場合は0x0が返されます。
identifier: aabbb8ca
parameters
_addr: Address being queried for the implementer of an interface. (If _addr == 0 them msg.sender is assumed.)
_interfaceHash: keccak256 hash of the name of the interface as a string. E.g. web3.utils.keccak256('ERC777Token')
returns: The address of the contract which implements the interface _interfaceHash for _addr or 0x0 if _addr did not register an implementer for this interface.
インターフェースの実装(ERC820ImplementerInterface)
interface ERC820ImplementerInterface {
/// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr`.
/// @param addr Address for which the contract will implement the interface
/// @param interfaceHash keccak256 hash of the name of the interface
/// @return ERC820_ACCEPT_MAGIC only if the contract implements `ìnterfaceHash` for the address `addr`.
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) public view returns(bytes32);
}
ERC820ImplementerInterfaceは、あるコントラクトが特定のアドレスに代わって特定のインターフェースをどう実装しているかを判定するための仕組みです。
このインターフェースを実装するコントラクトは、指定されたアドレスとインターフェースに関して、次の関数を提供する必要があります。
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) public view returns(bytes32);
この関数は、コントラクトが特定のアドレス(addr
)に対して特定のインターフェース(interfaceHash
)を実装しているかどうかを示します。
ここで、interfaceHash
はそのインターフェース名のkeccak256ハッシュ値です。
identifier: f0083250
parameters
interfaceHash: Hash of the interface which is implemented
addr: Address for which the interface is implemented
returns: ERC820_ACCEPT_MAGIC only if the contract implements ìnterfaceHash for the address addr.
もしコントラクトがそのアドレスに代わってインターフェースを実装している場合、関数はERC820_ACCEPT_MAGIC
という特別な値を返します。
これは、"ERC820_ACCEPT_MAGIC"という文字列のkeccak256ハッシュで定義されたものです。
逆に、そのインターフェースを実装していない場合は、ERC820_ACCEPT_MAGIC
以外の値を返します。
bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC");
真偽値ではなくERC820_ACCEPT_MAGIC
を使用する理由は、万が一canImplementInterfaceForAddress
が実装されておらず、フォールバック関数が無害に実行される場合、誤ってインターフェースが実装されていると見なされるのを防ぐためです。
あるコントラクトが特定のアドレスのために特定のインターフェースを実装しているかを確かめたい場合、このERC820ImplementerInterface
を実装し、適切な条件下でERC820_ACCEPT_MAGIC
を返すかどうかをチェックします。
The reason to return ERC820_ACCEPT_MAGIC instead of a boolean is to prevent cases where a contract fails to implement the canImplementInterfaceForAddress but implements a fallback function which does not throw. In this case, since canImplementInterfaceForAddress does not exist, the fallback function is called instead, executed without throwing and returns 1. Thus making it appear as if canImplementInterfaceForAddress returned true.
真偽値ではなくERC820_ACCEPT_MAGICを返す理由は、コントラクトがcanImplementInterfaceForAddressを実装していないが、例外を投げないフォールバック関数を実装している場合を防ぐためです。このケースでは、canImplementInterfaceForAddressが存在しないため、代わりにフォールバック関数が呼び出され、例外を投げずに1を返します。これにより、まるでcanImplementInterfaceForAddressが真を返したかのように見えてしまいます。
マネージャー
アドレス(個人のアカウントまたはコントラクト)に対して、特定のインターフェースの実装を登録できるのは、そのアドレスのマネージャーだけです。
通常、各アドレスは初期状態で自分自身がマネージャーとなっています。
setManager
function setManager(address _addr, address _newManager) public
setManager関数を使えば、あるアドレスのマネージャー権限を別のアドレスに移譲できます。
この操作により、新しいマネージャーはそのアドレスに関するインターフェースの実装をsetInterfaceImplementer
関数を通じて設定できるようになります。
もし新しいマネージャーを0x0
(つまり無効なアドレス)に設定すれば、マネージャーは再び元のアドレス自身にリセットされます。
identifier: 5df8122f
parameters
_addr: Address for which to set the new manager.
_newManager: The address of the new manager for _addr. (Pass 0x0 to reset the manager to _addr.)
getManager
function getManager(address _addr) public view returns(address)
また、getManager関数を使用すると、あるアドレスの現在のマネージャーが誰であるかを確認できます。
これにより、アドレスのマネージャーを知りたい場合や、マネージャーが変更されたかどうかを確認する場合に役立ちます。
identifier: 3d584063
parameters
_addr: Address for which to return the manager.
returns: Address of the manager for a given address.
これらの関数を適切に使用することで、アドレスのマネージャーを管理し、そのアドレスが特定のインターフェースをどのコントラクトで実装しているかを効率的に設定・確認することができます。
補足
この規格は、外部所有のアドレスやコントラクトなど、あらゆるタイプのアドレスがインターフェースを実装し、潜在的にプロキシコントラクトにインターフェースの実装を委任する方法を提供します。
このプロキシコントラクトへの委任は、外部所有アカウントにとって必要であり、マルチシグやDAOなどの既存コントラクトを再デプロイするのを避けるためにも有用です。
このレジストリはまた、コントラクトが特定のERC165インターフェースを実装しているかどうかを調べる時のガスを節約するためのERC165キャッシュとしても機能します。
このキャッシュは意図的にシンプルに保たれており、自動キャッシュ更新や無効化はありません。
誰でもupdateERC165Cache
関数を呼び出すことで、任意のインターフェースとコントラクトについてキャッシュを簡単かつ安全に更新できます。
レジストリは、誰もが制御しないことを保証し、信頼性を確保するために、一回限りのデプロイメントアドレスに依存するキーレスデプロイメント方法を使用してデプロイされます。
後方互換性
この規格はERC165と後方互換性があり、互いに矛盾することなく実装することができます。
テスト
テストコードは以下を参考にしてください。
参考実装
実装コードは以下を参考にしてください。
引用
Jordi Baylina jordi@baylina.cat, Jacques Dafflon jacques@dafflon.tech, "ERC-820: Pseudo-introspection Registry Contract," Ethereum Improvement Proposals, no. 820, January 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-820.
最後に
今回は「特定のアドレスのコントラクトが、特定の機能を実装しているかやどこで実装されているかの情報を取得できる仕組みを提案している規格であるERC820」についてまとめてきました!
いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!
他の媒体でも情報発信しているのでぜひ他も見ていってください!