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

介绍

作为一名区块链开发者或该领域的热衷者,我们应该对以太坊的高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" + "");

如果直接从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 keylet 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。恢复(消息、签名);

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篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

Layer 2道友 – 欢迎对Layer 2感兴趣的区块链技术爱好者、研究分析人与Gavin(微信: chinadefi)联系,共同探讨Layer 2带来的落地机遇。敬请关注我们的微信公众号 “去中心化金融社区”