最新消息:本站技术交流 QQ 群:28124927

消息签名与Permit

区块链/比特币 exchen 2886浏览 0评论

消息签名与Permit

ECR20 标准代币有一个 approve 函数,它可以将当前用户指定的币数授权给另一个账户操作,该函数定义如下:

调用 approve 函数必须是这个账户的 Owner 才可以执行操作,有没有一种办法像比特币那种使用账户的私钥离线签名,验证签名后就能执行操作,不需要直接使用 Owner 联网操作? permit 可以做到,这个函数是 ERC20 的扩展,定义如下:

在学习 permit 原理之前,有必要先了解 Solidity 如何打包与哈希运算、如何离线签名、如何验签等一些知识点。

打包与哈希运算

Solidity 一般是调用 keccak256 函数计算哈希。我们做一个测试,定义一个 getHash 的函数,接受一个字符串,将字符串使用 encodePacked 打包,然后再调 keccak256 即可获取字符串的哈希,代码如下:

encodePacked 是一个很有用的函数,它能够将任一数据,打包成十六进制数据,方便做进一步处理,比如将三个不同类型的变量数据加包在一起,代码如下:

执行之后,在 getHash 函数输入参数字符串 123,会看到返回的 Hash 值,在 encodePacked 函数里输入三个参数,分别是字符串 123、地址 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,数字 1,会看到打包后的结果,如下图所示:

消息签名与验签

编写一个合约用于验签,名称为 VerifySign,有 5 个函数,第一个函数名称是 getMessageHash,功能接受一个字符串参数获取哈希,在上面我们讲解了哈希的获取方法。第二个函数名称是 getEthSignedMessageHash,传入的参数是第一个函数获取到的哈希,这个函数的功能相当于获取了两次哈希。第三个函数名称是 getSigner,功能是获取签名者,返回签名者的地址,对比这个地址是否匹配即可验签。第四个函数名称是 splitSign,用于拆分签名数据,将签名数据拆分成 r,s,v 三个数据,这三个数据可以了解一下椭圆加密。第五个函数名称是 verify,就是判断签名者的地址是否与我们提供的地址一样,一样则代表验签成功,否则验签失败。具体代码如下:

验签的合约写好了,如何签名呢?在 geth 客户端里提供了签名的方法,加载 geth,先查看当前的账户,如果没有账户则新建

新建好账户后,需要先输入密码解锁账户

接下来布署合约,输入字符串 hello,记录返回的哈希数据

在 geth 客户端调用 web3 接口,指定 0x1f0dd411dca792d3cc502515a325755315365b52 账户给 hello 的哈希数据签名,返回结果如下:

将 geth 客户端返回的签名数据放入 getSigner 的参数 _sign,_ethSignedMessageHash 参数是对 messageHash 的第二参哈希,返回的结果是 0x1F0dD411dca792d3CC502515a325755315365B52,与 geth 客户端返的签名账户一致,说明验签成功。

![image-20220803164949764](/Users/geek/Library/Application Support/typora-user-images/image-20220803164949764.png)

也可以在 verify 方法里验证,提供相应的参数,返回 true 代表验签成功,如下图所示:

![image-20220803172312358](/Users/geek/Library/Application Support/typora-user-images/image-20220803172312358.png)

Permit

下面我们来看一下 permit 函数的实现,该函数所有文件是 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/draft-ERC20Permit.sol,有 6 个参数,第一个参数 owner 是被授权的地址,第二个参数 spender 是授权给谁,第三个参数 value 是授权币的数量,第四个参数 deadline 是一个时间戳,超过这个时间就无效,最后三个参数是拆分后的签名数据。其中 recover 这个函数是用于验签,返回 signer,将 signer 与 owner 做对比,一致则代表验签成功,不一致则验签失败,具体代码如下:

我们需要找一个币的合约代码来做测试,用这个币做测试,https://etherscan.io/address/0xD417144312DbF50465b1C641d016962017Ef6240,为了方便测试效果,我们把 permit 函数修改一下,添加一个 permit 只要验签成功则调用 _approve 授权,修改后的代码如下:

我们一直使用的是 remix 做测试,remix 上默认的账户如何导入到本地使用呢?需要找到它的私钥,通过了解发现 remix 的私钥是写死代码里的,可以查找 remix 的源码找到私钥,下面我们使用 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 这个地址来做测试。

在 geth 客户端使用 importRawKey 导入私钥,设置账户密码 123,然后再使用该账户签名 hello 这个字符串

需要将生成的签名数据进行拆分,还记得在上面的验签合约里的 splitSign 这个函数吗?它可以将签名拆分成 r, s, v 三个数据。

最后将相应的数据填写上,执行 permit2 无论是否使用 Owner 都可以执行这一笔授权操作,如下图所示:

为了确认授权是否成功,可以调用 ECR20 的 allowance 查看,返回的结果是 10,说明 permit2 是成功的。

转载请注明:exchen's blog » 消息签名与Permit

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址