Solidity中哈希能否逆向还原为原字符串及低Gas存长字符串方案
问题1:哈希逆向还原原始字符串是否可行?
完全不可行。keccak256是密码学安全的单向哈希函数,设计初衷就是保证无法通过输出的哈希值逆推出原始输入:它会把任意长度的输入压缩成固定32字节的输出,过程是单向、不可逆的,不存在可落地的算法能直接把bytes32格式的哈希还原成原始长字符串。同时理论上存在不同输入得到相同哈希的碰撞可能(实际概率极低可忽略),哪怕靠暴力枚举,只要原始字符串长度足够、内容未知,枚举成本高到完全不具备现实可行性。
你写的reverse函数从原理上就不可能实现预期效果。
问题2:长字符串低Gas存储的可行方案
核心思路是尽量减少链上storage的存储占用,EVM的storage操作是Gas消耗最高的操作之一,每个32字节的非零storage slot写入就要消耗20000左右Gas,长字符串会占用多个slot,成本自然很高,可按业务场景选以下方案:
- 方案1:链上仅存哈希校验,原始数据存链下(最省Gas,适用绝大多数场景)
你本来就要存哈希,完全不用在链上存原始字符串:把原始物理地址存在IPFS、前端静态资源、业务数据库这类链下存储里,链上只存对应的keccak256哈希值。需要校验地址合法性的时候,让用户把原始字符串作为参数传入合约,合约本地计算一次哈希和链上存的值比对,一致就说明传入的是正确的原始地址。这个方案链上只需要存1个bytes32的值,Gas成本只有存长字符串的几十分之一。 - 方案2:原始数据存事件日志,适合不需要合约内读取的场景
如果你需要链上留存原始字符串、但不需要在合约的业务逻辑里读取它做计算,那不要把字符串存在storage里,写入的时候直接触发事件把字符串存在日志里:日志的Gas成本只有storage的1/10不到,链下可以通过扫区块日志拿到完整的原始字符串,同时链上还是可以存对应的哈希做合法性校验。 - 方案3:固定内容抽成常量+紧凑编码,必须全量存链上时用
如果业务要求必须把完整原始字符串存在链上storage里,首先把地址里重复的固定前缀(比如统一的省市区前缀、国家邮编前缀)定义为constant常量,常量不会占用storage空间,编译时会直接嵌入合约字节码,读取几乎无Gas消耗,只存动态变化的部分;另外存储时做紧凑编码,把短字段尽量打包到同一个32字节slot里,减少slot占用数量,能降低30%-50%不等的存储成本。 - 方案4:枚举值映射,适合地址集合有限固定的场景
如果你要存的物理地址是提前已知、总量有限的集合(比如只存业务覆盖的几百个门店地址),可以给每个地址分配一个1-2字节的整数ID,链上只存这个ID,ID和原始地址的映射表存在前端/链下,使用时通过ID查对应原始地址即可,存储成本比存bytes32哈希还低。
参考代码示例(对应方案1)
你的原代码除了核心逻辑不可行外,还存在参数缺类型、返回值未定义的语法问题,可参考以下可运行的低Gas实现:
// 链上只存地址哈希,常量不占storage,几乎无额外成本 bytes32 public constant TARGET_ADDR_HASH = keccak256(abi.encodePacked("some large string")); // 不需要逆向哈希,传入原始字符串做链上校验即可 function verifyAddr(string calldata inputAddr) external pure returns(bool) { return keccak256(abi.encodePacked(inputAddr)) == TARGET_ADDR_HASH; }
内容的提问来源于stack exchange,提问作者Mohamad javad Safari
相关产品推荐
相关产品推荐

