许多在线ECDSA教程涉及到数学的使用,关于s, r, v的一些东西,我们所有的开发人员都同意,其是无聊的,并且很难在没有bug的情况下实现。因此,在本文中,我们将使用OpenZeppelin和Ethers.js编写的合约中的内置函数来构建这个功能。
作为一名区块链开发者或该领域的热衷者,我们应该对以太坊的高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,而不使用任何复杂的数学。然而,将计算置于链下也存在需要权衡的地方,但这超出了本文的范围。
ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!