ERC-820 의사 인트로스펙션 레지스트리 (Pseudo-introspection Registry Contract)
Final · 기타 · 2018-01-05 작성
어떤 주소든(컨트랙트, 일반 계정 모두) 자신이 어떤 인터페이스를 지원하는지, 그 구현을 어느 컨트랙트가 맡는지를 등록하고 누구나 조회할 수 있는 범용 레지스트리를 정의합니다.
왜 필요한가
이더리움에서 "이 주소가 어떤 기능을 지원하는가"를 알아내는 기존 방법에는 한계가 있었습니다. ERC-165는 컨트랙트가 스스로 지원 인터페이스를 답하는 방식이라, 코드가 없는 일반 계정(사람이 쓰는 지갑 주소)은 아예 쓸 수 없습니다. 또 다른 시도인 ERC-672는 역방향 이름 서비스(reverse ENS)를 이용했지만 불필요하게 복잡하고 ENS 자체가 멀티시그가 통제하는 중앙화된 컨트랙트라는 문제가 있었습니다. 이미 배포된 멀티시그나 DAO처럼 코드를 바꿀 수 없는 컨트랙트가 새 인터페이스를 지원하게 만들 방법도 없었습니다. 체인마다 레지스트리 주소가 달라지면 올바른 주소를 찾는 일도 문제가 됩니다. 이 표준은 완전히 탈중앙화된 단일 레지스트리를 모든 체인에서 같은 주소로 배포해 이 문제들을 풉니다.
구현해야 하는 것
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address): 어떤 주소가 특정 인터페이스를 지원하는지, 어느 컨트랙트가 구현하는지 조회합니다. 등록이 없으면 0x0을 돌려줍니다.function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external: 특정 주소를 위해 인터페이스를 구현하는 컨트랙트를 등록합니다. 그 주소의 매니저만 호출할 수 있습니다.function setManager(address _addr, address _newManager) external: 주소의 매니저를 새 주소로 바꿉니다. 새 매니저가 그 주소의 인터페이스 등록 권한을 갖습니다.function getManager(address _addr) public view returns(address): 주소의 매니저를 돌려줍니다. 따로 정하지 않았으면 그 주소 자신이 매니저입니다.function interfaceHash(string _interfaceName) external pure returns(bytes32): 인터페이스 이름 문자열의 keccak256 해시를 온체인에서 계산해 줍니다.function updateERC165Cache(address _contract, bytes4 _interfaceId) external: 어떤 컨트랙트가 ERC-165 인터페이스를 구현하는지 여부를 캐시에 갱신합니다. 누구나 호출할 수 있습니다.function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool): 컨트랙트가 ERC-165 인터페이스를 구현하는지 확인합니다. 캐시가 있으면 캐시를 사용합니다.function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool): 캐시를 읽지도 갱신하지도 않고 컨트랙트의 ERC-165 인터페이스 구현 여부를 직접 확인합니다.function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32): 대리 구현 컨트랙트가 특정 주소를 위해 해당 인터페이스를 구현하는 경우에만 ERC820_ACCEPT_MAGIC 값을 돌려줍니다.event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);: 어떤 주소의 인터페이스 구현자로 특정 컨트랙트가 등록되었음을 알립니다.event ManagerChanged(address indexed addr, address indexed newManager);: 어떤 주소의 매니저가 새 주소로 바뀌었음을 알립니다.
심층 설명
인터페이스 이름과 해시 규칙
인터페이스는 이름 문자열의 keccak256 해시로 식별합니다. 승인된 ERC의 인터페이스라면 이름을 반드시 ERC###XXXXX 형식(###은 ERC 번호, XXXXX는 CamelCase 인터페이스 이름)으로 짓습니다. 예로 keccak256("ERC20Token"), keccak256("ERC777Token"), keccak256("ERC777TokensSender"), keccak256("ERC777TokensRecipient")가 있습니다. 누구나 자기만의 인터페이스 이름을 만들어 쓸 수 있지만 예약된 명명 규칙과 충돌해서는 안 됩니다. 해시 계산은 레지스트리의 interfaceHash(string _interfaceName) 함수로 온체인에서도 할 수 있습니다.
등록 규칙과 ERC820_ACCEPT_MAGIC
setInterfaceImplementer는 그 주소의 매니저만 호출할 수 있습니다. _addr가 0이면 msg.sender로 간주하는데, 이는 멀티시그 인스턴스 주소와 무관하게 서명할 트랜잭션 데이터를 같게 만들어 멀티시그 사용을 단순하게 합니다. _addr와 _implementer가 다르면 _implementer는 ERC820ImplementerInterface를 구현해야 하고 canImplementInterfaceForAddress(interfaceHash, addr) 호출이 ERC820_ACCEPT_MAGIC(문자열 "ERC820_ACCEPT_MAGIC"의 keccak256 해시)을 돌려줘야만 등록됩니다. 불리언 대신 매직 값을 쓰는 이유는, 함수를 구현하지 않은 컨트랙트의 폴백 함수가 예외 없이 실행되어 1을 돌려주면 마치 true를 반환한 것처럼 보이는 오탐을 막기 위해서입니다. _interfaceHash는 ERC-165 인터페이스(뒤 28바이트가 0)여서는 안 됩니다.
매니저 제도
어떤 주소의 인터페이스 구현자는 그 주소의 매니저만 등록할 수 있습니다. 기본값으로 모든 주소는 자기 자신이 매니저입니다. setManager(_addr, _newManager)로 매니저 권한을 다른 주소에 넘길 수 있고 새 매니저가 그 주소에 대해 setInterfaceImplementer를 호출할 수 있게 됩니다. _newManager에 0x0을 넣으면 매니저가 _addr 자신으로 되돌아갑니다. getManager로 현재 매니저를 조회합니다.
ERC-165 호환과 캐시
해시의 마지막 28바이트가 0이면 ERC-165 인터페이스로 간주합니다. 이 경우 getInterfaceImplementer는 앞 4바이트를 ERC-165 인터페이스 ID로 보고 해당 컨트랙트에 직접 물어 지원 여부를 확인합니다. 레지스트리는 ERC-165 조회 결과를 캐시해 가스를 아끼는데, 자동 갱신이나 무효화는 없습니다. 컨트랙트가 인터페이스를 동적으로 바꾸고 이 캐시에 의존한다면 updateERC165Cache를 호출해 수동으로 갱신해야 하며 누구나 다른 컨트랙트를 대신해 갱신할 수 있습니다. 캐시를 거치지 않는 조회는 implementsERC165InterfaceNoCache로 합니다. 이 표준은 ERC-165와 하위 호환되며 ERC-165 호환 부분은 William Entriken과 함께 설계되었습니다.
키리스 배포로 모든 체인에서 같은 주소
레지스트리는 일회용 주소를 쓰는 키리스 배포 방식(Nick의 방법)으로 배포됩니다. 배포 트랜잭션은 어느 체인에서나 쓰일 수 있도록 EIP-155를 쓰지 않고 가스 가격은 100 Gwei로 높게 잡습니다. 서명의 v는 27, r과 s는 820이 반복되는 패턴으로 사람이 정한 값이라, 이 서명에서 역산한 일회용 배포 계정(0xE6C244a1C10Aa0085b0cf92f04cdaD947C2988b8)의 개인키는 아무도 모른다는 것이 보장됩니다. 이 계정에 정확히 0.08 이더를 먼저 보낸 뒤 트랜잭션을 브로드캐스트하면, 모든 체인에서 레지스트리 주소가 0x820b586C8C28125366C998641B09DCbE7d4cBF06으로 같아지고 누구도 그 주소에 다른 컨트랙트를 놓을 수 없습니다.
ERC-1820이 이 표준을 대체함
원문 상단 공지에 따르면 ERC-1820이 ERC-820을 대체했습니다. Solidity 0.5 업데이트로 생긴 ERC-165 로직의 비호환 문제를 ERC-1820이 고쳤고 그 수정을 빼면 ERC-1820은 ERC-820과 기능적으로 동일합니다. 원문은 ERC-820 대신 ERC-1820을 반드시 사용해야 한다고 명시합니다. 이 문서의 상태는 Final이지만 새 구현은 ERC-1820을 봐야 합니다.