本文介绍了EIP-1271标准,该标准允许智能合约验证签名,解决了智能合约账户没有私钥无法进行签名验证的问题。文章详细解释了EIP-1271的工作原理,并通过Safe(原Gnosis Safe)的实现案例,展示了如何在链上和链下验证消息的签名。最后,文章还讨论了_hash
参数的作用,并为开发者 如何在自己的合约中实施EIP-1271提供了指导。
EIP-1271 标准允许智能合约验证签名。
在本教程中,我们将概述数字签名、EIP-1271 的背景,以及 Safe(前身为 Gnosis Safe)使用的 EIP-1271 的具体实现。总而言之,这可以作为在你自己的合约中实现 EIP-1271 的起点。
在这种上下文中,签名(更准确地说,是“数字签名”)是一条消息,加上某种证明,证明该消息来自特定的人/发送者/地址。
例如,数字签名可能如下所示:
0x000…
0x000…
实际上创建了整个消息(这通常是某种加密方法)。重要的是要注意,数字签名包括“消息”和“签名”两者。
为什么?例如,如果你给了我一份合同让我签字,然后我剪掉了签名页,只把我的签名还给你,而没有合同的其余部分,那么这份合同将是无效的。
同样,没有相关消息,数字签名没有任何意义!
为了创建可在基于以太坊的区块链上使用的数字签名,通常需要一个其他人不知道的秘密私钥。这使得你的签名成为你的签名(没有秘密密钥的知识,其他人无法创建相同的签名)。
你的以太坊账户(即你的外部拥有的账户/EOA)有一个与之关联的私钥,这就是当网站或 DApp 要求你签名时(例如,用于“使用以太坊登录”)通常使用的私钥。
应用程序可以验证你创建的签名,使用像 ethers.js 这样的第三方库无需知道你的私钥,并且确信 你 是创建签名的人。
事实上,由于 EOA 数字签名使用公钥加密,它们可以离线生成和验证!这就是 Gasless DAO 投票的工作原理——不是在链上提交投票,而是可以使用加密库在链下创建和验证数字签名。
虽然 EOA 账户有私钥,但智能合约账户没有任何类型的私钥或密钥(因此“使用以太坊登录”等无法与智能合约账户原生工作)。
EIP-1271 旨在解决的问题:如果智能合约没有可以合并到签名中的“秘密”,我们如何判断智能合约签名是否有效?
智能合约没有可用于对消息进行签名的私钥。那么,我们如何判断签名是否真实?
嗯,一个想法是我们可以直接 询问 智能合约签名是否真实!
EIP-1271 所做的是标准化 “询问” 智能合约给定的签名是否有效的想法。
实现 EIP-1271 的合约必须具有一个名为 isValidSignature
的函数,该函数接受消息和签名。然后,合约可以运行一些验证逻辑(规范在此处不强制执行任何特定内容),然后返回一个值,指示签名是否有效。
如果 isValidSignature
返回有效结果,那么这几乎就是合约说“是的,我批准这个签名+消息!”
这是 EIP-1271 规范中的确切接口(我们将在下面讨论 _hash
参数,但就目前而言,可以将其视为要验证的消息):
pragma solidity ^0.5.0;
contract ERC1271 {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 constant internal MAGICVALUE = 0x1626ba7e;
/**
* @dev Should return whether the signature provided is valid for the provided hash
* @param _hash Hash of the data to be signed
* @param _signature Signature byte array associated with _hash
*
* MUST return the bytes4 magic value 0x1626ba7e when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
* MUST allow external calls
*/
function isValidSignature(
bytes32 _hash,
bytes memory _signature)
public
view
returns (bytes4 magicValue);
}
合约可以通过多种方式实现 isValidSignature
—— 规范仅针对具体实现没有做太多说明。
实现 EIP-1271 的一个值得注意的合约是 Safe(以前称为 Gnosis Safe)。
在 Safe 的代码中,isValidSignature
被实现,以便可以通过 两种方式创建和验证签名:
isValidSignature
,并将要验证的消息作为消息参数传递,并为签名参数传递 一个空值(即 0x
)。Safe 将看到签名参数为空,它不会以加密方式验证签名,而是会知道继续检查消息是否在“已批准”消息列表中。isValidSignature
。在消息参数中,传入要验证的消息。在签名参数中,传入每个 Safe 所有者的单独签名,所有签名首尾相连。Safe 将检查是否有足够的签名来满足阈值,并且每个签名都有效。如果是这样,它将返回一个值,指示签名验证成功。_hash
参数到底是什么?为什么不传递整个消息?你可能已经注意到,EIP-1271 接口 中的 isValidSignature
函数没有接收消息本身,而是接收 _hash
参数。这意味着,我们不是将完整的任意长度的消息传递给 isValidSignature
,而是传递消息的 32 字节哈希值(通常为 keccak256)。
calldata 的每个字节 —— 即传递给智能合约函数的函数参数数据 —— 花费 16 gas(如果为零字节,则花费 4 gas),所以如果消息很长,这可以节省大量 gas。
在野外存在 EIP-1271 规范,其 isValidSignature
函数的第一个参数类型为 bytes
(任意长度,而不是固定长度的 bytes32
),参数名称为 message
。这是 EIP-1271 标准的 旧版本。
这里的规范非常开放。Safe 实现有一些好主意:
最后,这取决于你作为合约开发人员!
EIP-1271 是一种通用的标准,允许智能合约验证签名。它为智能合约打开了更像 EOA 的大门 —— 例如,提供了一种让“使用以太坊登录”与智能合约一起工作的方式 —— 并且可以通过多种方式实现(Safe 有一个重要的、有趣的实现需要考虑)。
- 原文链接: ethereum.org/en/develope...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!