基于区块链的项目旨在实现的主要目标之一是数据验证。对于实例,您可以查看数字身份和在线文档存储和检查。事实上,任何这些情况都需要行动/交易发起者验证来确认个人或实体。例如,如果一个人拥有数字形式的身份证件,那么确保所有权就变得至关重要。因此,这是数据可验证性问题的一个很好的例子。让我们回顾一下最简单的解决方案——数字签名,其测试是智能合约开发过程中的关键点之一。
方法很简单:
1)系统按照众所周知的规则生成消息
2)签名者获取消息并添加一组特定的符号——数字签名,通过私钥从消息构造的代码
3) 生成的签名现在被发送到合约,在那里它被分解以检索签名者的地址。
Solidity 为您提供用于签名生成和进一步分解的 ECDSA 算法。我们不需要深入研究算法本身(你可以找到必要的信息 在适当的来源)。我们需要知道的是,ECDSA 是非对称加密的一个示例,其中第一个用户使用其私钥创建签名,第二个用户应用标准算法来检索签名者的公钥。因此,它可以验证签名的来源。相反,让我们关注实际部分——签名的使用和测试。
首先,我们需要认识到一个问题。例如,合约需要执行一些操作,比如存储调用者的地址。虽然合约只有在调用者经过验证的情况下才应该执行存储,但我们需要确保任何人都不能在未经许可的情况下使用他们的地址。为了检索真实的调用者,我们需要生成一些消息,对其进行签名,然后在合约中将其分解。
你可以找到 Solidity 文档中的标准解决方案 (例如, 在0.8.4 — 本文当前的最新稳定版本)。文档为我们提供了合约,它需要以下内置功能:消息生成、签名分割和用于检索签名者的汇编代码。该示例显示了所有必要的方法,并且非常简单,但它有两个缺点:缺乏通用性,并且没有解决方案测试的良好示例。这就是为什么我提供我的代码版本和(实际目标)——合约的测试策略。
当然,你可以使用 标准 OpenZeppelin 库 对于 ECDSA 操作,但您将再次面临同样的问题 - 缺乏灵活性和测试方法。那么,让我们深入研究我的基于签名的逻辑示例。你可以找到完整的 我的 GitHub 中的工作示例,但是想要完整展现的地方却很少。
首先,我们要准备消息。正如您所看到的,它由两个打包和散列的钱包地址组成:
第二个重要的代码是将消息与标准以太坊消息一起进行哈希处理:
它表明该消息是在以太坊网络内发送的,并且具有 32 字节长度,这不是随机数。经过前面的操作,我们得到了哈希值,其长度为 32 字节。以这种形式提供额外的哈希函数是至关重要的,我们稍后将讨论其推理。
其他代码片段非常标准。分割签名的函数如下:
这是检索签名者的函数:
对于外部接口,我们将使用自定义函数,该函数接收签名和必要的参数,检查用户是否已经注册,形成消息并验证签名:
首先,我们将模仿需要签名的消息。我们将使用 以太坊.js 图书馆,这是(连同 web3)最常用、最方便的库。由于它是一个开源库,您可以自由探索 它的代码和文档。此外,这个库为我们提供了构建以下消息的完美接口:
两者的缺点之一 web3 和 醚 库的缺点是它们不具备本地 Ganache 环境的所有功能,因为这两个库都旨在与完整的以太坊节点一起工作。尽管如此,有一种使用本地测试的方法 web3 帐户功能。尽管您需要创建一个额外的帐户,它将实现歌手功能并提供与当前 web3 提供商的连接:
现在我们已经生成并签署了消息。但是,这还不是全部。还有一些事情要谈。这两个库(web3 和 ethers)在创建签名之前都提供了额外的消息哈希处理。此外,该消息不仅经过哈希处理,还与我们之前看到的标准以太坊消息相结合:
这就是我们在合约中添加附加方法的原因。因此,如果您想使用自定义消息、跳过额外的哈希处理等,则需要创建签名功能的自定义版本。我们已经讨论了上面的原因 - 两个标准库都实现了签名消息的典型方法,只能通过覆盖功能来更改。
作为最后一步,让我们运行测试并检查它是否正常工作:
我们测试了签名生成、消息生成、签名批准和拒绝。所以这是一套非常完整的验证功能测试。