EIP-1271:签名和验证智能合约签名

本文介绍了EIP-1271标准,该标准允许智能合约验证签名,解决了智能合约账户没有私钥无法进行签名验证的问题。文章详细解释了EIP-1271的工作原理,并通过Safe(原Gnosis Safe)的实现案例,展示了如何在链上和链下验证消息的签名。最后,文章还讨论了_hash参数的作用,并为开发者 如何在自己的合约中实施EIP-1271提供了指导。

EIP-1271 标准允许智能合约验证签名。

在本教程中,我们将概述数字签名、EIP-1271 的背景,以及 Safe(前身为 Gnosis Safe)使用的 EIP-1271 的具体实现。总而言之,这可以作为在你自己的合约中实现 EIP-1271 的起点。

什么是签名?

在这种上下文中,签名(更准确地说,是“数字签名”)是一条消息,加上某种证明,证明该消息来自特定的人/发送者/地址。

例如,数字签名可能如下所示:

  1. 消息:“我想用我的以太坊钱包登录这个网站。”
  2. 签名者:我的地址是 0x000…
  3. 证明:这是一些证明我 0x000… 实际上创建了整个消息(这通常是某种加密方法)。

重要的是要注意,数字签名包括“消息”和“签名”两者。

为什么?例如,如果你给了我一份合同让我签字,然后我剪掉了签名页,只把我的签名还给你,而没有合同的其余部分,那么这份合同将是无效的。

同样,没有相关消息,数字签名没有任何意义!

为什么存在 EIP-1271?

为了创建可在基于以太坊的区块链上使用的数字签名,通常需要一个其他人不知道的秘密私钥。这使得你的签名成为你的签名(没有秘密密钥的知识,其他人无法创建相同的签名)。

你的以太坊账户(即你的外部拥有的账户/EOA)有一个与之关联的私钥,这就是当网站或 DApp 要求你签名时(例如,用于“使用以太坊登录”)通常使用的私钥。

应用程序可以验证你创建的签名,使用像 ethers.js 这样的第三方库无需知道你的私钥,并且确信 是创建签名的人。

事实上,由于 EOA 数字签名使用公钥加密,它们可以离线生成和验证!这就是 Gasless DAO 投票的工作原理——不是在链上提交投票,而是可以使用加密库在链下创建和验证数字签名。

虽然 EOA 账户有私钥,但智能合约账户没有任何类型的私钥或密钥(因此“使用以太坊登录”等无法与智能合约账户原生工作)。

EIP-1271 旨在解决的问题:如果智能合约没有可以合并到签名中的“秘密”,我们如何判断智能合约签名是否有效?

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);
}

EIP-1271 示例实现:Safe

合约可以通过多种方式实现 isValidSignature —— 规范仅针对具体实现没有做太多说明。

实现 EIP-1271 的一个值得注意的合约是 Safe(以前称为 Gnosis Safe)。

在 Safe 的代码中,isValidSignature 被实现,以便可以通过 两种方式创建和验证签名:

  1. 链上消息
    1. 创建:Safe 所有者创建一个新的 Safe 交易来“签署”消息,并将消息作为数据传递到交易中。一旦有足够的所有者签署交易以达到多重签名阈值,交易就会被广播并运行。在交易中,有一个 Safe 函数被调用,该函数会将消息添加到“已批准”消息列表中。
    2. 验证:在 Safe 合约上调用 isValidSignature,并将要验证的消息作为消息参数传递,并为签名参数传递 一个空值(即 0x)。Safe 将看到签名参数为空,它不会以加密方式验证签名,而是会知道继续检查消息是否在“已批准”消息列表中。
  2. 链下消息:
    1. 创建:Safe 所有者创建一个链下消息,然后让其他 Safe 所有者分别签署消息,直到有足够的签名来超过多重签名批准阈值。
    2. 验证:调用 isValidSignature。在消息参数中,传入要验证的消息。在签名参数中,传入每个 Safe 所有者的单独签名,所有签名首尾相连。Safe 将检查是否有足够的签名来满足阈值,并且每个签名都有效。如果是这样,它将返回一个值,指示签名验证成功。

_hash 参数到底是什么?为什么不传递整个消息?

你可能已经注意到,EIP-1271 接口 中的 isValidSignature 函数没有接收消息本身,而是接收 _hash 参数。这意味着,我们不是将完整的任意长度的消息传递给 isValidSignature,而是传递消息的 32 字节哈希值(通常为 keccak256)。

calldata 的每个字节 —— 即传递给智能合约函数的函数参数数据 —— 花费 16 gas(如果为零字节,则花费 4 gas),所以如果消息很长,这可以节省大量 gas。

之前的 EIP-1271 规范

在野外存在 EIP-1271 规范,其 isValidSignature 函数的第一个参数类型为 bytes(任意长度,而不是固定长度的 bytes32),参数名称为 message。这是 EIP-1271 标准的 旧版本

如何在我自己的合约中实现 EIP-1271?

这里的规范非常开放。Safe 实现有一些好主意:

  • 你可以将来自合约“所有者”的 EOA 签名视为有效。
  • 你可以存储已批准消息的列表,并且只将这些消息视为有效。

最后,这取决于你作为合约开发人员!

结论

EIP-1271 是一种通用的标准,允许智能合约验证签名。它为智能合约打开了更像 EOA 的大门 —— 例如,提供了一种让“使用以太坊登录”与智能合约一起工作的方式 —— 并且可以通过多种方式实现(Safe 有一个重要的、有趣的实现需要考虑)。

这个教程有帮助吗?

  • 原文链接: ethereum.org/en/develope...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
以太坊中文
以太坊中文
以太坊中文, 用中文传播以太坊的最新进展