本文介绍了 ERC-1271 标准,其核心是智能合约如何验证签名。文章详细阐述了 ERC-1271 的重要性、使用案例及其实施,将 EOA 和智能合约之间的签名逻辑进行了区分,清晰解答了许多常见的误解,并提供了一些代码示例进行支持。
Bolivia
0. 引言
1. ERC-1271
2. 返回值
3. 术语澄清
- 是 ERC-1271,而不是 EIP-1271。
- 签名还是不签名,这才是问题。
4. 应用案例
5. 总结
最近,我看到 Cow DAO 关于 ERC-1271 的讲座,不禁想:这个我从未听说过的 ERC-1271 是什么神奇的东西?
事实证明,它是一个非常简单的 ERC,但在设定通过智能合约(钱包)验证签名的标准时至关重要,这也是目前热门的话题。
由于已经有许多关于该主题的示例和实现方法的文章,本文仅仅是对我在审查资料时遇到的术语和概念混淆的(希望)简要说明。
在我们深入讨论之前,分享我最近喜欢的歌曲;总是能营造一种平静的氛围 😇
官方文档始终是最有帮助的资源:点击这里。
一些文章将 ERC-1271 的动机解释为模仿 EOA “签名消息”,因为智能合约没有私钥,因此不能“签名”消息。
不过,在我看来,更容易将 ERC-1271 理解为仅仅是 执行签名验证 的标准,现在先搁置合约“签名”的理念。
我将在 3. 术语澄清 部分解释原因!
在 ERC-1271 出现之前,没有关于合约如何验证签名的标准,缺乏标准可能导致各种非标准化的实现。
contract ERC1271 {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 constant internal MAGICVALUE = 0x1626ba7e;
function isValidSignature(
bytes32 _hash,
bytes memory _signature)
public
view
returns (bytes4 magicValue);
}
如上所示,ERC-1271 的接口。
我们可以观察到,只要一个合约实现 isValidSignature()
函数并在成功验证时返回函数选择器 0x1626ba7e
,它就符合 ERC-1271 合约的资格。isValidSignature()
中的逻辑是可定制的。
要验证签名,只需在合约上调用 isValidSignature()
。这就是 ERC-1271 的用途!
众所周知,ERC-1271 相对简单易懂。唯一的问题可能是:为什么返回值是 bytes4
而不是 boolean
?不都是约定在函数名称以 is
为前缀时返回 boolean
吗?
对于那些好奇的人,我们可以参阅原始讨论:点击这里。
TL;DR:返回特定值 0x1626ba7e
可以确保这个值是有意返回的,防止函数无意间返回 true
。
为了更详细的解释,让我们考虑回退函数,如原始线程中提到的那样。
如果我们在一个不符合 ERC-1271 的合约上调用 isValidSignature()
,意味着没有 isValidSignature()
,将会触发回退函数。由于回退函数是定制的,它可以返回任何东西,包括一个布尔值 true
。但是,不实现 ERC-1271 的回退函数不太可能无意中返回特定值 0x1626ba7e
。
以下是一个简化的代码片段,演示 isValidSignature()
的返回值为布尔值的情况:
contract Callee {
// fallback() 只能返回 (bytes memory),
// 所以我们必须包装布尔值
fallback(bytes calldata) external returns (bytes memory) {
bool result = true;
bytes memory toReturn = abi.encode(result);
return toReturn;
}
}
// 为了从回退函数获得布尔值作为返回值,
// 我们可以使用接口包装 Callee,
// 这样我们就不需要使用 Callee.call() 并解码 (bytes memory)
interface JustAnInterface {
function isValidSignature() external returns (bool);
}
contract Caller {
function triggerFallbackOnCallee(address callee) public returns (bool) {
JustAnInterface justAnInterface = JustAnInterface(callee);
bool result = justAnInterface.isValidSignature();
return result;
}
}
在这里,我们可以看到 Callee
不是一个 ERC-1271 合约,但它的回退函数总是返回一个布尔值(包装在 bytes memory
中)。因此,尽管在 Callee
中实际上没有进行任何签名验证,但它无论如何向 Caller.triggerFallbackOnCallee()
返回 true
,表示成功验证。这可能导致灾难性的后果,因为签名验证通常涉及真实的资金。
在我看来,对于回退函数的担忧确实是有效的,如上所示。但是,函数名称 isValidSignature()
的选择本可以改进,例如 getSelectorForValidSignature()
(虽然太长,可能不理想)。但是,
首先,根据惯例,EIP 通常用于以太坊协议级别的变更,而 ERC 则用于智能合约开发标准化。
尽管大多数人交替使用 EIP 和 ERC,但不要让 EIP-1271 的普遍用法误导你。
这里没有涉及协议升级,相反,开发者遵循 ERC-1271 的标准接口相应地开发合约钱包。
有趣的是,当我写关于 EIP-4626 通货膨胀攻击(应为 ERC-4626)时,我也没有注意到这个细微之处 🙈
其次,如上所述,一些文章将 ERC-1271 描述为使合约也能够“签名”消息。
然而,如果我们将“签名”严格定义为“使用私钥签名消息”,显然,合约将永远无法“签名”消息,因为它们没有也永远不会有私钥。
原因很简单:区块链是透明的;合约能在公共空间存储“私有”密钥吗?
因此,我相信,每当其他文章提到“合约签名消息”时,他们实际上指的是“通过调用 ERC-1271 合规合约上的 isValidSignature()
并输入包括由 EOA 签名的消息进行 tx 的发送”。
这真是一长串的澄清 😵💫
让我们看一下来自官方文档的说明。
这个函数应该由 想要签名消息的合约(例如 智能合约钱包,DAO,多签名钱包等)来实现。希望支持合约签名的应用程序应该在 签名者是合约 的情况下调用此方法。
由于智能合约钱包由 EOAs 控制,所以通过智能合约“签名”本质上还是 EOAs 代表智能合约钱包签名。
我可以想到的其他两个解释,为什么那些文章的作者用这样的措辞是:
你还能想出其他的想法吗?请与我分享 💡
由于 ERC-1271 涉及智能合约和签名验证,所有用例都涉及智能合约钱包也就不足为奇了! 好吧,如果涉及签名,就牵涉到所有权,那么所用的合约总是可以叫做钱包。
我强烈推荐阅读以下文章,因为它详细阐述了Safe如何实现签名和验证的两种方法:
isValidSignature()
查找已注册的签名。isValidSignature()
的函数来处理验证、定制逻辑以及可能的一些状态变化,以便一次性完成两个任务。再说一遍:智能合约没有私钥,所以它们不能像 EOAs 那样签名消息。通过 ERC-1271,有一个标准供用户(EOAs)签名消息,然后在符合 ERC-1271 的智能合约钱包上使用 isValidSignature()
验证签名!
老实说,开始时我由于多篇文章的组合而感到非常困惑:
因此,我想:是否发生过一次硬分叉,神奇地使得智能合约具备私钥? 🤯🤯🤯 那不可能!
事实证明,这一切都是误解 😇
以上就是对 ERC-1271 的澄清!我希望这篇文章能帮助任何在阅读这些伟大但稍微令人困惑的关于这个小主题的资料时感到困惑的人!
如果你想讨论或发现任何错误,请在下面留言!下次见!
非常感谢 NIC Lin 对代码片段和编辑的帮助!
- 原文链接: medium.com/taipei-ethere...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!