尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Solidity字符串比较的艺术:foundry-devops的StringUtils之keccak256哈希对比与contains实现

Solidity字符串比较的艺术:foundry-devops的StringUtils之keccak256哈希对比与contains实现 Solidity字符串比较的艺术foundry-devops的StringUtils之keccak256哈希对比与contains实现【免费下载链接】foundry-devops项目地址: https://gitcode.com/gh_mirrors/fo/foundry-devopsfoundry-devops 是面向 Solidity 开发者的 Foundry DevOps 工具箱既能从 broadcast 目录读取最近的部署记录也能判断当前是否处于 zkSync 链上详见 README.md。本文聚焦其中小巧而实用的 src/StringUtils.sol 字符串工具库——它用 keccak256 哈希对比实现了isEqualTo字符串比较并提供逐字节滑窗的contains子串查找是学习 Solidity 字符串处理的绝佳范例。 为什么 Solidity 不能直接用比较字符串老 Solidity 开发者都有切身体会编译器不允许对string类型直接使用/!一旦写下a b就会被无情驳回。想比较两个字符串通常有两条路方案写法思路评价转bytes后逐字节循环自己维护下标与长度代码冗长容易写出越界 bugkeccak256 哈希对比keccak256(abi.encodePacked(s1)) keccak256(abi.encodePacked(s2))✅ 社区通行写法简洁且天然防碰撞keccak256 方案的原理直白易懂abi.encodePacked把字符串压成原始字节序列keccak256 对其输出 256 位摘要相同字符串必然得到相同哈希不同字符串的碰撞概率约 1/2²⁵⁶可忽略不计。更妙的是整个函数可以标记为internal pure——不读不写任何状态编译器会将其内联调用开销几乎为零。这正是 foundry-devops 的isEqualTo所做的全部事情核心实现只有两行见 src/StringUtils.solfunction isEqualTo(string memory str1, string memory str2) internal pure returns (bool) { return keccak256(abi.encodePacked(str1)) keccak256(abi.encodePacked(str2)); }配合 Solidity 的方法语法糖调用时读起来就像语言内置功能一样自然using StringUtils for string; bool same DeployStuff.isEqualTo(DeployStuff); // true contains逐字节滑窗查找子串的实现解析contains回答的是另一个高频问题这段文本里是否包含某个子串其实现src/StringUtils.sol是一个教科书级的逐字节滑窗算法function contains(string memory str, string memory substr) internal pure returns (bool) { bytes memory strBytes bytes(str); bytes memory substrBytes bytes(substr); if (strBytes.length substrBytes.length || strBytes.length 0) return false; for (uint i 0; i strBytes.length - substrBytes.length; i) { bool isEqual true; for (uint j 0; j substrBytes.length; j) { if (strBytes[i j] ! substrBytes[j]) { isEqual false; break; } } if (isEqual) return true; } return false; }把流程翻译成大白话转换把两个字符串都转成bytes按字节处理提前拦截原串为空、或比子串还短直接返回false不做无用功滑窗窗口从第 0 位逐字节右移每移一位就对子串逐字节比对及时止损任意一个字节不匹配立刻break跳出内层循环节省 gas命中即返回整段子串完全匹配时立即返回true。几个容易踩坑的边界情况行为都定义得很清晰场景返回值说明原串为空false被开头的长度检查提前拦截子串比原串长false提前拦截窗口无需滑动子串为空原串非空true空子串被视为包含在任何非空字符串中子串确实存在true滑窗命中即返回子串不存在false全部窗口扫描完毕同样contains也是internal pure内联进调用方后不产生额外外部调用开销适合在循环里高频调用。 真实场景用它过滤 broadcast 部署记录字符串工具库的价值要在真实业务中才看得出。foundry-devops 的核心功能是get_most_recent_deployment扫描broadcast目录找出指定合约在某条链上的最近部署地址。StringUtils在其中承担了文件筛选器的角色src/DevOpsTools.solif ( normalizedPath.contains(string.concat(/, vm.toString(chainId), /)) normalizedPath.contains(.json) !normalizedPath.contains(dry-run) ) { string memory json vm.readFile(normalizedPath); latestAddress processRun(json, contractName, latestAddress); }三重contains过滤逻辑一目了然路径包含目标 chainId 目录、以.json结尾、且不是dry-run演练记录。随后解析部署 JSON 时则用isEqualTo精确匹配合约名src/DevOpsTools.solif (deployedContractName.isEqualTo(contractName)) { latestAddress json.readAddress( string.concat($.transactions[, vm.toString(i), ].contractAddress) ); }没有这两个函数同一段逻辑将堆满手动下标遍历与字节比对代码——哈希对比与滑窗查找让 DevOps 脚本保持了难得的简洁。项目配置中开启的fs_permissionsfoundry.toml也正是为读取这些部署文件服务的。 快速上手3 步在你的项目里用上 StringUtils第 1 步引入依赖在 Foundry 项目根目录执行forge install Cyfrin/foundry-devops --no-commit也支持git clone https://gitcode.com/gh_mirrors/fo/foundry-devops获取源码后手动引入。第 2 步导入并绑定方法语法import {StringUtils} from lib/foundry-devops/src/StringUtils.sol; contract MyContract { using StringUtils for string;注意其版本约束为 Solidity0.8.13 0.9.0主流版本均可直接使用。第 3 步像内置方法一样调用require(mainnet.isEqualTo(mainnet), chain mismatch); require(vm.envOr(RPC_URL, ).contains(rpc.), bad rpc url);测试侧的用法可参考 test/FoundryZkSyncCheckerTest.t.sol项目通过make test即可回归验证全部功能。✅ 小结isEqualTo用keccak256(abi.encodePacked(...))哈希对比优雅绕开 Solidity 无法直接字符串的限制两行代码、纯函数、零调用开销contains逐字节滑窗查找长度提前拦截 不匹配立即breakgas 友好边界行为清晰两个函数均为internal pure可被编译器内联是 Solidity 字符串处理的轻量级范本在 src/DevOpsTools.sol 中它们共同支撑了按链筛选部署记录、按名称匹配合约的核心流程。下次再遇到Solidity 怎么比较字符串答案就在 foundry-devops 的 src/StringUtils.sol 里。【免费下载链接】foundry-devops项目地址: https://gitcode.com/gh_mirrors/fo/foundry-devops创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表