Solidity智能合约中如何用私钥解密加密字符串实现乐透代币动态返回tokenURI
乐透类NFT盲盒解密方案实现
核心选型说明
你这个场景更适合用对称加密算法实现,不要用非对称加密的私钥:非对称加密私钥一旦上链公开就完全失去作用,而对称加密的密钥只需要在活动结束后公开,完全匹配你的需求,你参考逻辑里的privateKey实际对应对称加密的密钥。
具体实现步骤
- 链下预生成所有代币对应的原始URI,使用选定的对称加密算法(推荐AES-128-CTR,gas消耗低、解密速度快)和提前生成的密钥加密所有URI得到密文,将密文批量存入合约的
_tokenURIs映射中 - 活动未结束时,密钥字段为空,调用
tokenURI统一返回预设的盲盒默认URI - 所有代币售卖完成、活动正式结束后,管理员调用专用的密钥设置接口将对称密钥写入合约
- 密钥写入后,所有
tokenURI调用会自动运行解密逻辑,返回对应代币的真实URI
完整代码示例
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; // 这里导入已经过审计的Solidity AES实现库即可 import "./AES.sol"; contract LottoNFT is ERC721 { mapping(uint256 => string) private _tokenURIs; string public defaultUri; string public encryptionKey; address public owner; modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } constructor(string memory name_, string memory symbol_, string memory _defaultUri) ERC721(name_, symbol_) { defaultUri = _defaultUri; owner = msg.sender; } // 管理员批量设置加密后的URI function setBatchEncryptedURI(uint256[] calldata tokenIds, string[] calldata encryptedUris) external onlyOwner { require(tokenIds.length == encryptedUris.length, "Length mismatch"); for (uint256 i = 0; i < tokenIds.length; i++) { _tokenURIs[tokenIds[i]] = encryptedUris[i]; } } // 活动结束后管理员设置加密密钥 function revealKey(string calldata key) external onlyOwner { require(bytes(encryptionKey).length == 0, "Key already revealed"); encryptionKey = key; } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); if (bytes(encryptionKey).length == 0) { return defaultUri; } // 解密逻辑需和链下加密逻辑完全匹配,IV可选择拼接在密文前/后,解密时先拆分 return AES.decrypt(_tokenURIs[tokenId], encryptionKey); } }
关键注意事项
- 加密时用到的初始向量(IV)可以选择拼接在密文的开头或结尾,存入
_tokenURIs,解密时先拆分出IV再进行解密,避免固定IV被暴力破解 - 密钥写入合约后是完全公开的,必须确认所有代币都已售出、没有剩余未售代币后再调用
revealKey接口,避免用户提前根据解密后的URI筛选高价值代币 - 提前测试解密逻辑的gas消耗,避免URI过长时解密操作超出块gas限制导致
tokenURI调用失败 - 链下加密完成后务必先用测试环境验证链上解密结果和原始URI完全一致,再部署正式合约
内容的提问来源于stack exchange,提问作者ssk
相关产品推荐
相关产品推荐

