使用 OpenZeppelin 和 Ethers.js 在 Solidity 中使用 ECDSA 验证链下结果和白名单

许多在线ECDSA教程涉及到数学的使用,关于s, r, v的一些东西,我们所有的开发人员都同意,其是无聊的,并且很难在没有bug的情况下实现。因此,在本文中,我们将使用OpenZeppelin和Ethers.js编写的合约中的内置函数来构建这个功能。

0.png

介绍

作为一名区块链开发者或该领域的热衷者,我们应该对以太坊的高gas问题并不陌生。以太坊的价格坚挺在3000美元区域,而Gas 价格平均上涨 50-70 Gwei,每笔交易的 Gas 费用越来越贵,一次简单的转账大约需要 4 美元。

有一个解决gas问题的方法,就是把这个计算放到链下,让服务器来做这个工作。

许多在线ECDSA教程涉及到数学的使用,关于s, r, v的一些东西,我们所有的开发人员都同意,其是无聊的,并且很难在没有bug的情况下实现。因此,在本文中,我们将使用OpenZeppelin和Ethers.js编写的合约中的内置函数来构建这个功能。

实际的例子

在这个项目中,我们将使用ECDSA的一个常见用例来演示这个方法,就是为NFT项目设置白名单,并包含代码片段来帮助我们入门。

这个项目是用JavaScript和Solidity编写的。

1. 设置

为了准备ECDSA,我们应该创建一个新的钱包,并仅将其用于该项目作为签名者。请勿将此钱包用于任何其他目的,而仅用于签名此项目中的消息。

创建钱包后,保存其私钥以供以后使用。

2. 链下签名

2.1 首先,我们需要运行以下命令来安装Ether.js:

npm run ethers

然后通过以下方式导入到项目中:

import ethers from ethers

2.2 然后,我们可以使用库创建一个新的Wallet来初始化签名者实例:

const signer = new ethers.Wallet("0x" + "<your private key>");

如果直接从Metamask导出,请记住在私钥的前缀中添加0x。

2.3 将消息打包在一起,为了白名单我们可以尝试打包地址和nonce:

let message = ethers.utils.solidityPack(["address", "uint256"], ["0xabc", "0"]);

这是为了将消息连接在一起,以便在下一节中进行散列。ether .js支持广泛的变量,包括string和数组,如`uint256[]:

2.4 使用keccak256对消息进行哈希,并使用签名者钱包进行签名:

message = ethers.utils.solidityKeccak256(["bytes"], [message]);
const signature = await

signer.signMessage(ethers.utils.arrayify(message));

此签名是使用签名者的私钥为消息签名的签名。

我们可以将此签名与已验证的参数一起传递到区块链,以确保参数是有效的。

整个代码片段:

const signer = new ethers.Wallet("0x" + "abc"); // replace abc with your private key
let message = ethers.utils.solidityPack(["address", "uint256"], ["0xabc", "0"]);
message = ethers.utils.solidityKeccak256(["bytes"], [message]);
const signature = await signer.signMessage(ethers.utils.arrayify(message));

3. 链上验证

3.1 为了验证链上签名,我们可以使用OpenZeppelin编写的合约EDCSA。要使用它,就需要在本地安装Openzepplin或在Remix中使用它:

npm install @openzeppelin/contracts

3.2 使用setter为链上签名者设置存储:

address signer;function setSigner(address _signer) external {     
    signer = _signer;
}

3.3 然后用abi.encodePacked将这些值打包在一起并使用keccack256对其进行散列:

bytes32 hash = keccak256(abi.encodePacked(msg.sender, nonce));

3.4 将签名转换为以太坊签名的消息:

bytes32 message = ECDSA.toEthSignedMessageHash(hash);

3.5 从签名中恢复签名者地址:

address receivedAddress = ECDSA.recover(message, signature);

<br/>

3.6 检查消息的签名者是否与链上的签名者存储库匹配,只有在匹配的情况下才批准:

require(receivedAddress != address(0) && receivedAddress == signer);

整个代码片段是:

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

...
address signer;

function setSigner(address _signer) external {
      signer = _signer;
}

...

function verifySignature(uint256 nonce, bytes calldata signature) public {
      bytes32 hash = keccak256(abi.encodePacked(msg.sender, nonce));
      bytes32 message = ECDSA.toEthSignedMessageHash(hash);
      address receivedAddress = ECDSA.recover(message, signature);
      require(receivedAddress != address(0) && receivedAddress == signer);
}

结论

现在我们学会了如何尽可能简单地使用 ECDSA,而不使用任何复杂的数学。然而,将计算置于链下也存在需要权衡的地方,但这超出了本文的范围。

Source:https://blog.cabala.co/how-to-verify-off-chain-results-and-whitelist-with-ecdsa-in-solidity-using-openzeppelin-ethers-js-ba4c85521711

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

本文首发于:https://mp.weixin.qq.com/s/X6ZnjfjwKGr2rN_SO8zsjw

点赞 1
收藏 3
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
ChinaDeFi 去中心化金融社区
ChinaDeFi 去中心化金融社区
ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。