本文深入探讨了 ERC-1820 注册合约,解释了其架构、参与者(管理器、实现者、目标和用户)以及它们之间的交互方式。文章还阐述了 ERC-1820 如何与 ERC-165 兼容,并介绍了 ERC-1820 的无密钥部署过程,确保了该合约在所有 EVM 链上具有相同的地址和可信度。
在写一篇关于 ERC777 的文章时,我不得不解释 ERC1820,在这个过程中,我意识到 ERC1820 需要一篇单独的文章。所以让我们更深入地研究 ERC-1820。
顾名思义,“注册合约” 允许任何合约或 EOA 注册它们支持的接口,并且这可以被其他合约/EOA 检查。
我们已经有了一个 ERC,ERC165,用于这个目的,但是它应该由每个合约单独实现,而 ERC1820 的实现方式不同。让我们看看它是如何工作的。首先,如果你想了解更多关于 ERC165 的技术细节,那就去看看我的 ERC721 文章。
正如我们所知,ERC165 由每个合约单独实现,ERC1820 提出了在每个链上都拥有一个单一的注册合约,并且拥有相同的合约地址的想法,并且每个其他合约都必须在这个注册合约中注册它们正在实现的任何接口。之后,任何用户都可以直接从注册合约查询某个特定的接口是否已经被某个合约实现,以及哪个智能合约处理它的实现。当我们继续前进时,会变得更加清晰。
它包含 4 个主要参与者:管理者(manager)、实现者(implementer)、目标(target)和用户(user)。
流程大致如下:
管理者(Manager)— 函数 getManager 返回地址的当前管理者,如果没有设置管理者,它会返回地址本身,这意味着每个地址都拥有自身的管理权。现在,地址或其管理者可以使用 setManager 函数来更改管理者。
function setManager(address _addr, address _newManager) external {
require(getManager(_addr) == msg.sender, "Not the manager");
managers[_addr] = _newManager == _addr ? address(0) : _newManager;
emit ManagerChanged(_addr, _newManager);
}
function getManager(address _addr) public view returns (address) {
if (managers[_addr] == address(0)) {
return _addr;
} else {
return managers[_addr];
}
}
现在,管理者可以调用 setInterfaceImplementer 函数,并为目标地址设置实现者。现在让我们看看这个函数内部是什么。
function setInterfaceImplementer(
address _addr,
bytes32 _interfaceHash,
address _implementer
) external {
address addr = _addr == address(0) ? msg.sender : _addr;
require(getManager(addr) == msg.sender, "Not the manager");
require(
!isERC165Interface(_interfaceHash),
"Must not be an ERC165 hash"
);
if (_implementer != address(0) && _implementer != msg.sender) {
require(
ERC1820ImplementerInterface(_implementer)
.canImplementInterfaceForAddress(_interfaceHash, addr) ==
ERC1820_ACCEPT_MAGIC,
"Does not implement the interface"
);
}
interfaces[addr][_interfaceHash] = _implementer;
emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
}
setInterfaceImplementer 首先定义目标地址,它检查是否已经传递了目标地址,如果没有,那么 msg.sender 成为目标。只有当调用者是给定地址的管理者时,它才会继续执行。
现在来到了一个重要的部分,该函数确保这不是一个 ERC165 接口 ID,否则会回退。
这是通过检查给定的接口 ID 是否以 28 个零结尾来完成的。原因是 ERC165 接口是一个 4 字节的值,而 ERC1820 接口 ID 是一个 32 字节的值。
在这里 了解更多关于我们如何计算 ERC165 接口 ID 的信息。
另一方面,ERC1820 接口 ID 只是接口名称的 keccak256 哈希。
function interfaceHash(string calldata _interfaceName)
external
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(_interfaceName));
}
现在我们知道了两个接口 ID 之间的区别,我们可以看看这个函数:
function isERC165Interface(bytes32 _interfaceHash)
internal
pure
returns (bool)
{
return
_interfaceHash &
0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ==
0;
}
如果传递的接口 ID 以 28 个零结尾,则此函数将返回 true。表明该接口 ID 与 ERC165 兼容。
如果给定的 ID 是 ERC165 ID,则该函数会回退,否则它会检查实现者的地址是否为零地址,或者 msg.sender 本身是否为实现者,如果这两个条件都为 false,则它会有一个 require 语句来检查实现者的地址是否已经实现了给定的接口 ID。
它通过调用给定实现者地址的函数 canImplementInterfaceForAddress() 来完成此操作。如果它实现了该接口,则此函数应返回 ERC1820_ACCEPT_MAGIC。
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
一旦通过此检查,interfaces 映射将使用目标地址、接口 ID 和实现者地址进行更新。最后,我们发出事件 InterfaceImplementerSet。
现在让我们看看 getInterfaceImplementer 函数。
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash)
external
view
returns (address)
{
address addr = _addr == address(0) ? msg.sender : _addr;
if (isERC165Interface(_interfaceHash)) {
bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
return
implementsERC165Interface(addr, erc165InterfaceHash)
? addr
: address(0);
}
return interfaces[addr][_interfaceHash];
}
它的开头也与 setter 函数相同。区别在于当传递的接口 ID 是 ERC165 ID 时。它没有回退,而是调用了 implementsERC165Interface 函数,我们稍后会讨论它。在相反的情况下,它只返回 interfaces 映射中的值,这就是调用者确认该地址是否实现了该接口的方式。
现在让我们退一步,看看当接口 ID 是 ERC165 ID 时会发生什么。
首先,我们将列出所有与 ERC165 相关的函数。
function updateERC165Cache(address _contract, bytes4 _interfaceId)
external
{
interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
_contract,
_interfaceId
)
? _contract
: address(0);
erc165Cached[_contract][_interfaceId] = true;
}
function implementsERC165Interface(address _contract, bytes4 _interfaceId)
public
view
returns (bool)
{
if (!erc165Cached[_contract][_interfaceId]) {
return implementsERC165InterfaceNoCache(_contract, _interfaceId);
}
return interfaces[_contract][_interfaceId] == _contract;
}
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;
}
第一个函数 updateERC165Cache 用于使用 erc165Cached 映射将接口 ID 直接记录到注册表中。我们称之为 缓存。
缓存只不过是我们用来描述 ERC165 ID 直接存储在注册合约的存储中的术语,这样注册表就不需要调用目标合约来查询相同的信息。
函数 implementsERC165Interface 用于检查给定的地址是否实现了给定的接口。
它首先检查 ID 是否缓存在映射中,如果是,则简单地返回存储在 erc165Cached 映射中的目标地址,否则它会调用 implementsERC165InterfaceNoCache。
implementsERC165InterfaceNoCache 函数直接调用给定的地址,就像 ERC165 通常工作的方式一样。它根据收到的数据返回 true 或 false。
这就是 ERC1820 如何向后兼容 ERC165 的。
在此处 查找完整的合约。
现在你了解了此合约的逻辑,你可能想知道我们如何在每个链上都有一个单一的合约。谁将部署该合约?我们又该如何就哪个合约将被视为相应区块链上的通用注册合约达成一致?
当我们了解 ERC1820 的部署过程时,事情变得有趣起来。
要理解这部分,你应该对以太坊区块链中使用的 ECDSA 签名机制有一定的了解。不深入细节,我将提供一些要点。
一个交易由以下参数组成: Nonce,Gas price,Gas limit,Recipient,Value,Data,v,r 和 s。在正常情况下,我们使用我们的私钥对交易进行签名,并获取 v,r,s 参数,并将所有参数发送到网络,其中 v,r 和 s 用于验证交易是否由正确的私钥签名。
但是在无密钥部署的情况下,我们不签名交易,而是为 v,r 和 s 提供任意值。这意味着我们没有任何私钥,我们只是生成了一个签名,而实际上并不知道私钥。我们永远不会知道私钥,因为 r,s,v 无法还原以获取私钥。
为了更好地理解,让我们看一下为部署 ERC1820 创建的实际交易对象。
const rawTransaction = {
nonce: 0,
gasPrice: 100000000000,
value: 0,
data: '0x' + contracts.ERC1820Registry.ERC1820Registry.bin,
gasLimit: 800000,
v: 27,
r: '0x1820182018201820182018201820182018201820182018201820182018201820',
s: '0x1820182018201820182018201820182018201820182018201820182018201820'
};
你可以在这里找到它。
正如我们在此交易中看到的,v,r 和 s 的值是由人明确编写的。
现在有了这些随机输入,我们可以获取将要发送此 tx 的地址。请记住,我们无法从这些数据中获取私钥。
下一步是为恢复的地址提供资金,以便它可以支付交易费用。
然后将此交易发送到区块链。此交易将部署合约,并且没有人拥有用于部署它的 EOA 的私钥。
我们可以在所有 EVM 链上执行此操作,并在所有链上的相同地址 0x1820a4b7618bde71dce8cdc73aab6c95905fad24 上部署注册合约。
现在你了解了你需要了解的关于 ERC1820 的所有知识。如果你有任何问题或讨论点,请加入我们这里。
- 原文链接: decipherclub.com/erc-182...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!